diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..79bc206 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: phanatagama # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +ko_fi: phanatagama # Replace with a single Ko-fi username \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..2a83b7c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,43 @@ +name: Builds +on: + push: + branches: + - main + pull_request: + +# Declare default permissions as read only. +permissions: read-all + +jobs: + build: + name: Build ${{ matrix.target }} + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + target: ["apk --debug", "appbundle --debug", "ios --no-codesign", macos] + steps: + - name: Set up JDK 11 + uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0 + with: + java-version: 11 + distribution: temurin + # Set up Flutter. + - name: Clone Flutter repository with master channel + uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa + with: + channel: master + - run: flutter doctor -v + + # Checkout gallery code and get packages. + - name: Checkout gallery code + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - run: flutter pub get + + - run: flutter build ${{ matrix.target }} + + # Upload generated apk to the artifacts. + - uses: actions/upload-artifact@v2 + with: + name: debug-apk + path: build/app/outputs/apk/debug/app-debug.apk \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..d037e12 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,86 @@ +name: Tests +on: + push: + branches: + - main + pull_request: + +# Declare default permissions as read only. +permissions: read-all + +jobs: + unit-test: + name: Unit tests on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + steps: + # Set up Flutter. + - name: Clone Flutter repository with master channel + uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa + with: + channel: master + - run: flutter doctor -v + + # Checkout gallery code and get packages. + - name: Checkout gallery code + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - run: flutter pub get + + # Analyze, check formatting, and run unit tests. + - run: flutter analyze + - name: Ensure the Dart code is formatted correctly + run: dart format --set-exit-if-changed . + - name: Run Flutter unit tests + run: flutter test --coverage + + # Upload coverage to codecov + - name: Upload coverage to codecov + uses: codecov/codecov-action@v2.1.0 + with: + token: ${{secrets.CODECOV_TOKEN}} + file: ./coverage/lcov.info + + benchmark-test: + name: Benchmark tests + runs-on: ubuntu-latest + steps: + # Set up Flutter. + - name: Clone Flutter repository with master channel + uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa + with: + channel: master + - run: flutter doctor -v + + # Checkout gallery code and get packages. + - name: Checkout gallery code + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - run: flutter pub get + + - run: flutter test test_benchmarks + + golden-test: + name: Golden tests + runs-on: macos-latest + steps: + # Set up Flutter. + - name: Clone Flutter repository with master channel + uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa + with: + channel: master + - run: flutter doctor -v + + # Checkout gallery code and get packages. + - name: Checkout gallery code + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - run: flutter pub get + + # Run the golden tests and upload failed test artifacts. + - run: flutter test test_goldens + - name: Upload goldens if tests fail + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce + if: failure() + with: + name: goldens + path: test_goldens/failures/ \ No newline at end of file diff --git a/README.md b/README.md index cc18969..92bc6a1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ ![Suji](https://socialify.git.ci/phanatagama/suji/image?description=1&font=Rokkitt&forks=1&issues=1&language=1&name=1&pulls=1&stargazers=1&theme=Auto) +[![codecov](https://codecov.io/gh/phanatagama/suji/branch/main/graph/badge.svg?token=F777RVVH55)](https://codecov.io/gh/phanatagama/suji)

diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8649ed4..cdacd56 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + + \ No newline at end of file diff --git a/android/app/src/main/res/raw/mecca_56_22.mp3 b/android/app/src/main/res/raw/mecca_56_22.mp3 new file mode 100644 index 0000000..df755a9 Binary files /dev/null and b/android/app/src/main/res/raw/mecca_56_22.mp3 differ diff --git a/assets/Home.gif b/assets/Home.gif new file mode 100644 index 0000000..5fbd5eb Binary files /dev/null and b/assets/Home.gif differ diff --git a/assets/Prayer Time.gif b/assets/Prayer Time.gif new file mode 100644 index 0000000..f443313 Binary files /dev/null and b/assets/Prayer Time.gif differ diff --git a/assets/Search.gif b/assets/Search.gif new file mode 100644 index 0000000..53872a0 Binary files /dev/null and b/assets/Search.gif differ diff --git a/assets/Surah.gif b/assets/Surah.gif new file mode 100644 index 0000000..0cd9631 Binary files /dev/null and b/assets/Surah.gif differ diff --git a/assets/images/icon-prayer-time.png b/assets/images/icon-prayer-time.png new file mode 100644 index 0000000..1e92278 Binary files /dev/null and b/assets/images/icon-prayer-time.png differ diff --git a/assets/images/icon-quran.png b/assets/images/icon-quran.png new file mode 100644 index 0000000..528e021 Binary files /dev/null and b/assets/images/icon-quran.png differ diff --git a/assets/images/icon-shalat.png b/assets/images/icon-shalat.png new file mode 100644 index 0000000..dcc556a Binary files /dev/null and b/assets/images/icon-shalat.png differ diff --git a/assets/images/icon-tasbih.png b/assets/images/icon-tasbih.png new file mode 100644 index 0000000..900702d Binary files /dev/null and b/assets/images/icon-tasbih.png differ diff --git a/assets/images/masjid.jfif b/assets/images/masjid.jfif new file mode 100644 index 0000000..19a0148 Binary files /dev/null and b/assets/images/masjid.jfif differ diff --git a/assets/images/tasbih (1).png b/assets/images/tasbih (1).png new file mode 100644 index 0000000..53b166a Binary files /dev/null and b/assets/images/tasbih (1).png differ diff --git a/assets/model/asmaul_husna.json b/assets/model/asmaul_husna.json new file mode 100644 index 0000000..f4a92ea --- /dev/null +++ b/assets/model/asmaul_husna.json @@ -0,0 +1,599 @@ +{ + "dataAsmaulHusna": [ + { + "arabic": "الرَّحْمَنُ", + "index": "1", + "latin": "Ar Rahman", + "translation_en": "The All Beneficent", + "translation_id": "Yang Maha Pengasih" + }, { + "arabic": "الرَّحِيمُ", + "index": "2", + "latin": "Ar Rahiim", + "translation_en": "The Most Merciful", + "translation_id": "Yang Maha Penyayang" + }, { + "arabic": "الْمَلِكُ", + "index": "3", + "latin": "Al Malik", + "translation_en": "The King, The Sovereign", + "translation_id": "Yang Maha Merajai/Memerintah" + }, { + "arabic": "الْقُدُّوسُ", + "index": "4", + "latin": "Al Quddus", + "translation_en": "The Most Holy", + "translation_id": "Yang Mahasuci" + }, { + "arabic": "السَّلاَمُ", + "index": "5", + "latin": "As Salaam", + "translation_en": "Peace and Blessing", + "translation_id": "Yang Maha Memberi Kesejahteraan" + }, { + "arabic": "الْمُؤْمِنُ", + "index": "6", + "latin": "Al Mu’min", + "translation_en": "The Guarantor", + "translation_id": "Yang Maha Memberi Keamanan" + }, { + "arabic": "الْمُهَيْمِنُ", + "index": "7", + "latin": "Al Muhaimin", + "translation_en": "The Guardian, the Preserver", + "translation_id": "Yang Maha Pemelihara" + }, { + "arabic": "الْعَزِيزُ", + "index": "8", + "latin": "Al ‘Aziiz", + "translation_en": "The Almighty, the Self Sufficient", + "translation_id": "Yang Memiliki Mutlak Kegagahan" + }, { + "arabic": "الْجَبَّارُ", + "index": "9", + "latin": "Al Jabbar", + "translation_en": "The Powerful, the Irresistible", + "translation_id": "Yang Maha Perkasa" + }, { + "arabic": "الْمُتَكَبِّرُ", + "index": "10", + "latin": "Al Mutakabbir", + "translation_en": "The Tremendous", + "translation_id": "Yang Maha Megah" + }, { + "arabic": "الْخَالِقُ", + "index": "11", + "latin": "Al Khaliq", + "translation_en": "The Creator", + "translation_id": "Yang Maha Pencipta" + }, { + "arabic": "الْبَارِئُ", + "index": "12", + "latin": "Al Baari’", + "translation_en": "The Maker", + "translation_id": "Yang Maha Melepaskan" + }, { + "arabic": "الْمُصَوِّرُ", + "index": "13", + "latin": "Al Mushawwir", + "translation_en": "The Fashioner of Forms", + "translation_id": "Yang Maha Membentuk Rupa (makhluknya)" + }, { + "arabic": "الْغَفَّارُ", + "index": "14", + "latin": "Al Ghaffaar", + "translation_en": "The Ever Forgiving", + "translation_id": "Yang Maha Pengampun" + }, { + "arabic": "الْقَهَّارُ", + "index": "15", + "latin": "Al Qahhaar", + "translation_en": "The All Compelling Subduer", + "translation_id": "Yang Maha Memaksa" + }, { + "arabic": "الْوَهَّابُ", + "index": "16", + "latin": "Al Wahhaab", + "translation_en": "The Bestower", + "translation_id": "Yang Maha Pemberi Karunia" + }, { + "arabic": "الرَّزَّاقُ", + "index": "17", + "latin": "Ar Razzaaq", + "translation_en": "The Ever Providing", + "translation_id": "Yang Maha Pemberi Rezeki" + }, { + "arabic": "الْفَتَّاحُ", + "index": "18", + "latin": "Al Fattaah", + "translation_en": "The Opener, the Victory Giver", + "translation_id": "Yang Maha Pembuka Rahmat" + }, { + "arabic": "اَلْعَلِيْمُ", + "index": "19", + "latin": "Al ‘Aliim", + "translation_en": "The All Knowing, the Omniscient", + "translation_id": "Yang Maha Mengetahui (Memiliki Ilmu)" + }, { + "arabic": "الْقَابِضُ", + "index": "20", + "latin": "Al Qaabidh", + "translation_en": "The Restrainer, the Straightener", + "translation_id": "Yang Maha Menyempitkan (makhluknya)" + }, { + "arabic": "الْبَاسِطُ", + "index": "21", + "latin": "Al Baasith", + "translation_en": "The Expander, the Munificent", + "translation_id": "Yang Maha Melapangkan (makhluknya)" + }, { + "arabic": "الْخَافِضُ", + "index": "22", + "latin": "Al Khaafidh", + "translation_en": "The Abaser", + "translation_id": "Yang Maha Merendahkan (makhluknya)" + }, { + "arabic": "الرَّافِعُ", + "index": "23", + "latin": "Ar Raafi’", + "translation_en": "The Exalter", + "translation_id": "Yang Maha Meninggikan (makhluknya)" + }, { + "arabic": "الْمُعِزُّ", + "index": "24", + "latin": "Al Mu’izz", + "translation_en": "The Giver of Honor", + "translation_id": "Yang Maha Memuliakan (makhluknya)" + }, { + "arabic": "المُذِلُّ", + "index": "25", + "latin": "Al Mudzil", + "translation_en": "The Giver of Dishonor", + "translation_id": "Yang Maha Menghinakan (makhluknya)" + }, { + "arabic": "السَّمِيعُ", + "index": "26", + "latin": "Al Samii’", + "translation_en": "The All Hearing", + "translation_id": "Yang Maha Mendengar" + }, { + "arabic": "الْبَصِيرُ", + "index": "27", + "latin": "Al Bashiir", + "translation_en": "The All Seeing", + "translation_id": "Yang Maha Melihat" + }, { + "arabic": "الْحَكَمُ", + "index": "28", + "latin": "Al Hakam", + "translation_en": "The Judge, the Arbitrator", + "translation_id": "Yang Maha Menetapkan" + }, { + "arabic": "الْعَدْلُ", + "index": "29", + "latin": "Al ‘Adl", + "translation_en": "The Utterly Just", + "translation_id": "Yang Mahaadil" + }, { + "arabic": "اللَّطِيفُ", + "index": "30", + "latin": "Al Lathiif", + "translation_en": "The Subtly Kind", + "translation_id": "Yang Maha Lembut" + }, { + "arabic": "الْخَبِيرُ", + "index": "31", + "latin": "Al Khabiir", + "translation_en": "The All Aware", + "translation_id": "Yang Maha Mengetahui Rahasia" + }, { + "arabic": "الْحَلِيمُ", + "index": "32", + "latin": "Al Haliim", + "translation_en": "The Forbearing, the Indulgent", + "translation_id": "Yang Maha Penyantun" + }, { + "arabic": "الْعَظِيمُ", + "index": "33", + "latin": "Al ‘Azhiim", + "translation_en": "The Magnificent, the Infinite", + "translation_id": "Yang Maha Agung" + }, { + "arabic": "الْغَفُورُ", + "index": "34", + "latin": "Al Ghafuur", + "translation_en": "The All Forgiving", + "translation_id": "Yang Maha Pengampun" + }, { + "arabic": "الشَّكُورُ", + "index": "35", + "latin": "As Syakuur", + "translation_en": "The Grateful", + "translation_id": "Yang Maha Pembalas Budi (Menghargai)" + }, { + "arabic": "الْعَلِيُّ", + "index": "36", + "latin": "Al ‘Aliy", + "translation_en": "The Sublimely Exalted", + "translation_id": "Yang Maha Tinggi" + }, { + "arabic": "الْكَبِيرُ", + "index": "37", + "latin": "Al Kabiir", + "translation_en": "The Great", + "translation_id": "Yang Maha Besar" + }, { + "arabic": "الْحَفِيظُ", + "index": "38", + "latin": "Al Hafizh", + "translation_en": "The Preserver", + "translation_id": "Yang Maha Menjaga" + }, { + "arabic": "المُقيِت", + "index": "39", + "latin": "Al Muqiit", + "translation_en": "The Nourisher", + "translation_id": "Yang Maha Pemberi Kecukupan" + }, { + "arabic": "الْحسِيبُ", + "index": "40", + "latin": "Al Hasiib", + "translation_en": "The Reckoner", + "translation_id": "Yang Maha Membuat Perhitungan" + }, { + "arabic": "الْجَلِيلُ", + "index": "41", + "latin": "Al Jaliil", + "translation_en": "The Majestic", + "translation_id": "Yang Maha Mulia" + }, { + "arabic": "الْكَرِيمُ", + "index": "42", + "latin": "Al Kariim", + "translation_en": "The Bountiful, the Generous", + "translation_id": "Yang Maha Pemurah" + }, { + "arabic": "الرَّقِيبُ", + "index": "43", + "latin": "Ar Raqiib", + "translation_en": "The Watchful", + "translation_id": "Yang Maha Mengawasi" + }, { + "arabic": "الْمُجِيبُ", + "index": "44", + "latin": "Al Mujiib", + "translation_en": "The Responsive, the Answerer", + "translation_id": "Yang Maha Mengabulkan" + }, { + "arabic": "الْوَاسِعُ", + "index": "45", + "latin": "Al Waasi’", + "translation_en": "The Vast, the All Encompassing", + "translation_id": "Yang Maha Luas" + }, { + "arabic": "الْحَكِيمُ", + "index": "46", + "latin": "Al Hakiim", + "translation_en": "The Wise", + "translation_id": "Yang Maha Maka Bijaksana" + }, { + "arabic": "الْوَدُودُ", + "index": "47", + "latin": "Al Waduud", + "translation_en": "The Loving, the Kind One", + "translation_id": "Yang Maha Pencinta" + }, { + "arabic": "الْمَجِيدُ", + "index": "48", + "latin": "Al Majiid", + "translation_en": "The All Glorious", + "translation_id": "Yang Maha Mulia" + }, { + "arabic": "الْبَاعِثُ", + "index": "49", + "latin": "Al Baa’its", + "translation_en": "The Raiser of the Dead", + "translation_id": "Yang Maha Membangkitkan" + }, { + "arabic": "الشَّهِيدُ", + "index": "50", + "latin": "As Syahiid", + "translation_en": "The Witness", + "translation_id": "Yang Maha Menyaksikan" + }, { + "arabic": "الْحَقُّ", + "index": "51", + "latin": "Al Haqq", + "translation_en": "The Truth, the Real", + "translation_id": "Yang Mahabenar" + }, { + "arabic": "الْوَكِيلُ", + "index": "52", + "latin": "Al Wakiil", + "translation_en": "The Trustee, the Dependable", + "translation_id": "Yang Maha Memelihara" + }, { + "arabic": "الْقَوِيُّ", + "index": "53", + "latin": "Al Qawiyyu", + "translation_en": "The Strong", + "translation_id": "Yang Maha Kuat" + }, { + "arabic": "الْمَتِينُ", + "index": "54", + "latin": "Al Matiin", + "translation_en": "The Firm, the Steadfast", + "translation_id": "Yang Maha Kokoh" + }, { + "arabic": "الْوَلِيُّ", + "index": "55", + "latin": "Al Waliyy", + "translation_en": "The Protecting Friend, Patron, and Helper", + "translation_id": "Yang Maha Melindungi" + }, { + "arabic": "الْحَمِيدُ", + "index": "56", + "latin": "Al Hamiid", + "translation_en": "The All Praiseworthy", + "translation_id": "Yang Maha Terpuji" + }, { + "arabic": "الْمُحْصِي", + "index": "57", + "latin": "Al Mushii", + "translation_en": "The Accounter, the Numberer of All", + "translation_id": "Yang Maha Mengalkulasi" + }, { + "arabic": "الْمُبْدِئُ", + "index": "58", + "latin": "Al Mubdi’", + "translation_en": "The Producer, Originator, and Initiator of all", + "translation_id": "Yang Maha Memulai" + }, { + "arabic": "الْمُعِيدُ", + "index": "59", + "latin": "Al Mu’iid", + "translation_en": "The Reinstater Who Brings Back All", + "translation_id": "Yang Maha Mengembalikan Kehidupan" + }, { + "arabic": "الْمُحْيِي", + "index": "60", + "latin": "Al Muhyii", + "translation_en": "The Giver of Life", + "translation_id": "Yang Maha Menghidupkan" + }, { + "arabic": "اَلْمُمِيتُ", + "index": "61", + "latin": "Al Mumiitu", + "translation_en": "The Bringer of Death, the Destroyer", + "translation_id": "Yang Maha Mematikan" + }, { + "arabic": "الْحَيُّ", + "index": "62", + "latin": "Al Hayyu", + "translation_en": "The Ever Living", + "translation_id": "Yang Maha Hidup" + }, { + "arabic": "الْقَيُّومُ", + "index": "63", + "latin": "Al Qayyuum", + "translation_en": "The Self Subsisting Sustainer of All", + "translation_id": "Yang Maha Mandiri" + }, { + "arabic": "الْوَاجِدُ", + "index": "64", + "latin": "Al Waajid", + "translation_en": "The Perceiver, the Finder, the Unfailing", + "translation_id": "Yang Maha Penemu" + }, { + "arabic": "الْمَاجِدُ", + "index": "65", + "latin": "Al Maajid", + "translation_en": "The Illustrious, the Magnificent", + "translation_id": "Yang Maha Mulia" + }, { + "arabic": "الْواحِدُ", + "index": "66", + "latin": "Al Wahiid", + "translation_en": "The One, The Unique, Manifestation of Unity", + "translation_id": "Yang Maha Tunggal" + }, { + "arabic": "اَلاَحَدُ", + "index": "67", + "latin": "Al ‘Ahad", + "translation_en": "The One, the All Inclusive, the Indivisible", + "translation_id": "Yang Maha Esa" + }, { + "arabic": "الصَّمَدُ", + "index": "68", + "latin": "As Shamad", + "translation_en": "The Self Sufficient, the Impregnable,the Eternally Besought of All, the Everlasting", + "translation_id": "Yang Maha Dibutuhkan, Tempat Meminta" + }, { + "arabic": "الْقَادِرُ", + "index": "69", + "latin": "Al Qaadir", + "translation_en": "The All Able", + "translation_id": "Yang Maha Menentukan, Maha Menyeimbangkan" + }, { + "arabic": "الْمُقْتَدِرُ", + "index": "70", + "latin": "Al Muqtadir", + "translation_en": "The All Determiner, the Dominant", + "translation_id": "Yang Maha Berkuasa" + }, { + "arabic": "الْمُقَدِّمُ", + "index": "71", + "latin": "Al Muqaddim", + "translation_en": "The Expediter, He who brings forward", + "translation_id": "Yang Maha Mendahulukan" + }, { + "arabic": "الْمُؤَخِّرُ", + "index": "72", + "latin": "Al Mu’akkhir", + "translation_en": "The Delayer, He who puts far away", + "translation_id": "Yang Maha Mengakhirkan" + }, { + "arabic": "الأوَّلُ", + "index": "73", + "latin": "Al Awwal", + "translation_en": "The First", + "translation_id": "Yang Maha Awal" + }, { + "arabic": "الآخِرُ", + "index": "74", + "latin": "Al Aakhir", + "translation_en": "The Last", + "translation_id": "Yang Maha Akhir" + }, { + "arabic": "الظَّاهِرُ", + "index": "75", + "latin": "Az Zhaahir", + "translation_en": "The Manifest; the All Victorious", + "translation_id": "Yang Maha Nyata" + }, { + "arabic": "الْبَاطِنُ", + "index": "76", + "latin": "Al Baathin", + "translation_en": "The Hidden; the All Encompassing", + "translation_id": "Yang Maha Ghaib" + }, { + "arabic": "الْوَالِي", + "index": "77", + "latin": "Al Waali", + "translation_en": "The Patron", + "translation_id": "Yang Maha Memerintah" + }, { + "arabic": "الْمُتَعَالِي", + "index": "78", + "latin": "Al Muta’aalii", + "translation_en": "The Self Exalted", + "translation_id": "Yang Maha Tinggi" + }, { + "arabic": "الْبَرُّ", + "index": "79", + "latin": "Al Barri", + "translation_en": "The Most Kind and Righteous", + "translation_id": "Yang Maha Penderma" + }, { + "arabic": "التَّوَابُ", + "index": "80", + "latin": "At Tawwaab", + "translation_en": "The Ever Returning, Ever Relenting", + "translation_id": "Yang Maha Penerima Tobat" + }, { + "arabic": "الْمُنْتَقِمُ", + "index": "81", + "latin": "Al Muntaqim", + "translation_en": "The Avenger", + "translation_id": "Yang Maha Penuntut Balas" + }, { + "arabic": "العَفُوُّ", + "index": "82", + "latin": "Al Afuww", + "translation_en": "The Pardoner, the Effacer of Sins", + "translation_id": "Yang Maha Pemaaf" + }, { + "arabic": "الرَّؤُوفُ", + "index": "83", + "latin": "Ar Ra`uuf", + "translation_en": "The Compassionate, the All Pitying", + "translation_id": "Yang Maha Pengasih" + }, { + "arabic": "مَالِكُ الْمُلْكِ", + "index": "84", + "latin": "Malikul Mulk", + "translation_en": "The Owner of All Sovereignty", + "translation_id": "Yang Maha Penguasa Kerajaan (Semesta)" + }, { + "arabic": "ذُوالْجَلاَلِوَالإكْرَامِ", + "index": "85", + "latin": "Dzul JalaaliWal Ikraam", + "translation_en": "The Lord of Majesty and Generosity", + "translation_id": "Yang Maha Pemilik Kebesaran dan Kemuliaan" + }, { + "arabic": "الْمُقْسِطُ", + "index": "86", + "latin": "Al Muqsith", + "translation_en": "The Equitable, the Requiter", + "translation_id": "Yang Maha Adil" + }, { + "arabic": "الْجَامِعُ", + "index": "87", + "latin": "Al Jamii’", + "translation_en": "The Gatherer, the Unifier", + "translation_id": "Yang Maha Mengumpulkan" + }, { + "arabic": "الْغَنِيُّ", + "index": "88", + "latin": "Al Ghaniyy", + "translation_en": "The All Rich, the Independent", + "translation_id": "Yang Maha Berkecukupan" + }, { + "arabic": "الْمُغْنِي", + "index": "89", + "latin": "Al Mughnii", + "translation_en": "The Enricher, the Emancipator", + "translation_id": "Yang Maha Memberi Kekayaan" + }, { + "arabic": "اَلْمَانِعُ", + "index": "90", + "latin": "Al Maani", + "translation_en": "The Withholder, the Shielder, the Defender", + "translation_id": "Yang Maha Mencegah" + }, { + "arabic": "الضَّارَّ", + "index": "91", + "latin": "Ad Dhaar", + "translation_en": "The Distressor, the Harmer", + "translation_id": "Yang Maha Memberi Derita" + }, { + "arabic": "النَّافِعُ", + "index": "92", + "latin": "An Nafii’", + "translation_en": "The Propitious, the Benefactor", + "translation_id": "Yang Maha Memberi Manfaat" + }, { + "arabic": "النُّورُ", + "index": "93", + "latin": "An Nuur", + "translation_en": "The Light", + "translation_id": "Yang Maha Bercahaya (Menerangi, Memberi Cahaya)" + }, { + "arabic": "الْهَادِي", + "index": "94", + "latin": "Al Haadii", + "translation_en": "The Guide", + "translation_id": "Yang Maha Pemberi Petunjuk" + }, { + "arabic": "الْبَدِيعُ", + "index": "95", + "latin": "Al Baadii", + "translation_en": "Incomparable, the Originator", + "translation_id": "Yang Maha Pencipta" + }, { + "arabic": "اَلْبَاقِي", + "index": "96", + "latin": "Al Baaqii", + "translation_en": "The Ever Enduring and Immutable", + "translation_id": "Yang Maha Kekal" + }, { + "arabic": "الْوَارِثُ", + "index": "97", + "latin": "Al Waarits", + "translation_en": "The Heir, the Inheritor of All", + "translation_id": "Yang Maha Pewaris" + }, { + "arabic": "الرَّشِيدُ", + "index": "98", + "latin": "Ar Rasyiid", + "translation_en": "The Guide, Infallible Teacher, and Knower", + "translation_id": "Yang Maha Pandai" + }, { + "arabic": "الصَّبُورُ", + "index": "99", + "latin": "As Shabuur", + "translation_en": "The Patient", + "translation_id": "Yang Maha Sabar" + } + ] +} \ No newline at end of file diff --git a/assets/model/daily_pray.json b/assets/model/daily_pray.json new file mode 100644 index 0000000..28891f6 --- /dev/null +++ b/assets/model/daily_pray.json @@ -0,0 +1,214 @@ +{ + "dataDailyPray": [ + { + "title": "Doa Sebelum Makan", + "arabic": "اَللّٰهُمَّ بَارِكْ لَنَا فِيْمَا رَزَقْتَنَا وَقِنَا عَذَابَ النَّارِ", + "latin": "Alloohumma barik lanaa fiimaa razatanaa waqinaa 'adzaa bannar", + "translation": "Ya Allah, berkahilah kami dalam rezeki yang telah Engkau berikan kepada kami dan peliharalah kami dari siksa api neraka" + }, + { + "title": "Doa Sesudah Makan", + "arabic": "اَلْحَمْدُ ِللهِ الَّذِىْ اَطْعَمَنَا وَسَقَانَا وَجَعَلَنَا مُسْلِمِيْنَ", + "latin": "Alhamdu lillaahil ladzii ath'amanaa wa saqoonaa wa ja'alnaa muslimiin", + "translation": "Segala puji bagi Allah yang telah memberi makan kami dan minuman kami, serta menjadikan kami sebagai orang-orang islam" + }, + { + "title": "Doa Sesudah Minum", + "arabic": "اَلْحَمْدُ ِللهِ الَّذِىْ جَعَلَهُ عَذْبًا فُرَاتًا بِرَحْمَتِهِ وَلَمْ يَجْعَلْهُ مِلْحًا اُجَاجًا بِذُنُوْبِنَا", + "latin": "Alhamdu lillaahil ladzi ja'alahuu 'adzbam furootam birohmatihii wa lamyaj'alhu milhan ujaajam bidzunuubinaa", + "translation": "Segala puji bagi Allah yang telah menjadikan air ini (minuman) segar dan menggiatkan dengan rahmat-Nya dan tidak menjadikan air ini (minuman) asin lagi pahit karena dosa-dosa kami" + }, + { + "title": "Doa Ketika Makan Lupa Membaca Doa", + "arabic": "بِسْمِ اللهِ فِىِ أَوَّلِهِ وَآخِرِهِ", + "latin": "Bismillaahi fii awwalihi wa aakhirihi", + "translation": "Dengan menyebut nama Allah pada awal dan akhirnya" + }, + { + "title": "Doa Sebelum Tidur", + "arabic": "بِسْمِكَ اللّٰهُمَّ اَحْيَا وَاَمُوْتُ", + "latin": "Bismikallohumma ahya wa amuutu", + "translation": "Dengan menyebut nama-Mu ya Allah, aku hidup dan aku mati" + }, + { + "title": "Doa Ketika Mimpi Buruk", + "arabic": "اَللّٰهُمَّ إِنّىِ أَعُوْذُ بِكَ مِنْ عَمَلِ الشَّيْطَانِ وَسَيِّئاَتِ اْلأَحْلاَمِ", + "latin": "Allaahumma innii a'uudzubika min 'amalisy syaithaani wa sayyiaatil ahlami", + "translation": "Ya Allah, sesungguhnya aku mohon perlindungan kepada Engkau dari perbuatan setan dan dari mimpi-mimpi yang buruk" + }, + { + "title": "Doa Ketika Mendapat Mimpi Baik", + "arabic": "اَلْحَمْدُ ِللهِ الَّذِيْ قَطْلَ الْحَاجَتِ", + "latin": "Alhamdulillahil ladzii qodzoo haajaati", + "translation": "Segala puji bagi Allah yang telah memberi hajatku" + }, + { + "title": "Doa Bangun Tidur", + "arabic": "اَلْحَمْدُ ِللهِ الَّذِىْ اَحْيَانَا بَعْدَمَآ اَمَاتَنَا وَاِلَيْهِ النُّشُوْرُ", + "latin": "Alhamdu lillahil ladzii ahyaanaa ba'da maa amaa tanaa wa ilahin nusyuuru", + "translation": "Segala puji bagi Allah yang telah menghidupkan kami sesudah kami mati (membangunkan dari tidur) dan hanya kepada-Nya kami dikembalikan" + }, + { + "title": "Doa Masuk Kamar Mandi Atau Toilet", + "arabic": "اَللّٰهُمَّ اِنّىْ اَعُوْذُبِكَ مِنَ الْخُبُثِ وَالْخَبَآئِثِ", + "latin": "Alloohumma Innii a'uudzubika minal khubutsi wal khoaaitsi", + "translation": "Ya Allah, aku berlindung pada-Mu dari godaan syetan laki-laki dan setan perempuan" + }, + { + "title": "Doa Istinja", + "arabic": "اَللّٰهُمَّ حَسِّنْ فَرْجِىْ مِنَ الْفَوَاخِشِ وَظَهِّرْ قَلْبِيْ مِنَ النِّفَاقِ", + "latin": "Alloohumma thahhir qolbii minan nifaaqi wa hashshin fajrii minal fawaahisyi", + "translation": "Wahai Tuhanku, sucikanlah hatiku dari sifat kepura-puraan (munafiq) serta peliharalah kemaluanku dari perbuatan keji" + }, + { + "title": "Doa Keluar Kamar Mandi Atau Toilet", + "arabic": "غُفْرَانَكَ الْحَمْدُ ِللهِ الَّذِىْ اَذْهَبَ عَنّى اْلاَذَى وَعَافَانِىْ", + "latin": "Ghufraanaka. Alhamdulillaahil ladzii adzhaba ‘annjil adzaa wa’aafaanii", + "translation": "Dengan mengharap ampunanMu, segala puji milik Allah yang telah menghilangkan kotoran dari badanku dan yang telah menyejahterakan" + }, + { + "title": "Doa Menjelang Sholat Shubuh", + "arabic": "اَللّٰهُمَّ اِنِّى اَعُوْذُ بِكَ مِنْ ضِيْقِ الدُّنْيَا وَضِيْقِ يَوْمِ الْقِيَامَةِ", + "latin": "Alloohumma inni a'udzubika min dzhiiqid-dunyaa wa dzhiiqi yaumal-qiyaamati", + "translation": "Ya Allah, Sesungguhnya aku berlindung kepada-Mu dari kesempitan dunia dan kesempitan hari kiamat. (HR. Abu Daud)" + }, + { + "title": "Doa Menyambut Pagi hari", + "arabic": "اَللّٰهُمَّ بِكَ اَصْبَحْنَا وَبِكَ اَمْسَيْنَا وَبِكَ نَحْيَا وَبِكَ نَمُوْتُ وَاِلَيْكَ النُّشُوْرُ", + "latin": "Alloohumma bika ashbahnaa wa bika amsainaa wa bika nahyaa wa bika namuutu wa ilaikan nusyuuru", + "translation": "Ya Allah, karena Engkau kami mengalami waktu pagi dan waktu petang, dan karena Engkau kami hidup dan mati dan kepada-Mu juga kami akan kembali" + }, + { + "title": "Doa Menyambut Sore Hari", + "arabic": "اَللّٰهُمَّ بِكَ اَمْسَيْنَا وَبِكَ اَصْبَحْنَا وَبِكَ نَحْيَا وَبِكَ نَمُوْتُ وَاِلَيْكَ الْمَصِيْرُ", + "latin": "Alloohumma bika amsainaa wa bika ashbahnaa wa bika nahyaa wa bika namuutu wa ilaikal mashiir", + "translation": "Ya Allah, karena Engkau kami mengalami waktu petang dan waktu pagi, karena Engkau kami hidup dan mati dan kepada-Mu juga kami akan kembali" + }, + { + "title": "Doa Ketika Bercermin", + "arabic": "اَلْحَمْدُ ِللهِ كَمَا حَسَّنْتَ خَلْقِىْ فَحَسِّـنْ خُلُقِىْ", + "latin": "Alhamdulillaahi kamaa hassanta kholqii fahassin khuluqii", + "translation": "Segala puji bagi Allah, baguskanlah budi pekertiku sebagaimana Engkau telah membaguskan rupa wajahku" + }, + { + "title": "Doa Masuk Rumah", + "arabic": "اَللّٰهُمَّ اِنّىْ اَسْأَلُكَ خَيْرَالْمَوْلِجِ وَخَيْرَالْمَخْرَجِ بِسْمِ اللهِ وَلَجْنَا وَبِسْمِ اللهِ خَرَجْنَا وَعَلَى اللهِ رَبّنَا تَوَكَّلْنَا", + "latin": "Allahumma innii as-aluka khoirol mauliji wa khoirol makhroji bismillaahi wa lajnaa wa bismillaahi khorojnaa wa'alallohi robbina tawakkalnaa", + "translation": "Ya Allah, sesungguhnya aku mohon kepada-Mu baiknya tempat masuk dan baiknya tempat keluar dengan menyebut nama Allah kami masuk, dan dengan menyebut nama Allah kami keluar dan kepada Allah Tuhan kami, kami bertawakkal" + }, + { + "title": "Doa Keluar Rumah / Doa Bepergian", + "arabic": "بِسْمِ اللهِ تَوَكَّلْتُ عَلَى اللهِ لاَحَوْلَ وَلاَقُوَّةَ اِلاَّ بِالله", + "latin": "Bismillaahi tawakkaltu 'alalloohi laa hawlaa walaa quwwata illaa bilaahi", + "translation": "Dengan menyebut nama Allah aku bertawakal kepada Allah, tiada daya kekuatan melainkan dengan pertologan Allah" + }, + { + "title": "Doa Memakai Pakaian", + "arabic": "بِسْمِ اللهِ اَللّٰهُمَّ اِنِّى اَسْأَلُكَ مِنْ خَيْرِهِ وَخَيْرِ مَاهُوَ لَهُ وَاَعُوْذُبِكَ مِنْ شَرِّهِ وَشَرِّمَا هُوَلَهُ", + "latin": "Bismillaahi, Alloohumma innii as-aluka min khoirihi wa khoiri maa huwa lahuu wa'a'uu dzubika min syarrihi wa syarri maa huwa lahuu", + "translation": "Dengan nama-Mu yaa Allah akku minta kepada Engkau kebaikan pakaian ini dan kebaikan apa yang ada padanya, dan aku berlindung kepada Engkau dari kejahatan pakaian ini dan kejahatan yang ada padanya" + }, + { + "title": "Doa Memakai Pakaian Baru", + "arabic": "اَلْحَمْدُ لِلّٰهِ الَّذِىْ كَسَانِىْ هَذَا وَرَزَقَنِيْهِ مِنْ غَيْرِ حَوْلٍ مِنِّىْ وَلاَقُوَّةٍ", + "latin": "Alhamdu lillaahil ladzii kasaanii haadzaa wa rozaqoniihi min ghoiri hawlim minni wa laa quwwatin", + "translation": "Segala puji bagi Allah yang memberi aku pakaian ini dan memberi rizeki dengan tiada upaya dan kekuatan dariku" + }, + { + "title": "Doa Melepas Pakaian", + "arabic": "بِسْمِ اللهِ الَّذِيْ لاَ إِلَهَ إِلَّا هُوَ", + "latin": "Bismillaahil ladzii laa ilaaha illaa huwa", + "translation": "Dengan nama Allah yang tiada Tuhan selain-Nya" + }, + { + "title": "Doa Memohon Ilmu Yang Bermanfaat", + "arabic": "اَللّٰهُمَّ اِنِّى اَسْأَلُكَ عِلْمًا نَافِعًا وَرِزْقًا طَيِّبًا وَعَمَلاً مُتَقَبَّلاً", + "latin": "Alloohumma innii as-aluka 'ilmaan naafi'aan wa rizqoon thoyyibaan wa 'amalaan mutaqobbalaan", + "translation": "Ya Allah, sesungguhnya aku mohon kepada-Mu ilmu yang berguna, rezki yang baik dan amal yang baik Diterima. (H.R. Ibnu Majah)" + }, + { + "title": "Doa Sebelum Belajar", + "arabic": "يَارَبِّ زِدْنِىْ عِلْمًا وَارْزُقْنِىْ فَهْمًا", + "latin": "Yaa robbi zidnii 'ilman warzuqnii fahmaa", + "translation": "Ya Allah, tambahkanlah aku ilmu dan berikanlah aku rizqi akan kepahaman" + }, + { + "title": "Doa Sesudah Belajar", + "arabic": "اَللّٰهُمَّ اِنِّى اِسْتَوْدِعُكَ مَاعَلَّمْتَنِيْهِ فَارْدُدْهُ اِلَىَّ عِنْدَ حَاجَتِىْ وَلاَ تَنْسَنِيْهِ يَارَبَّ الْعَالَمِيْنَ", + "latin": "Allaahumma innii astaudi'uka maa 'allamtaniihi fardud-hu ilayya 'inda haajatii wa laa tansaniihi yaa robbal 'alamiin", + "translation": "Ya Allah, sesungguhnya aku menitipkan kepada Engkau ilmu-ilmu yang telah Engkau ajarkan kepadaku, dan kembalikanlah kepadaku sewaktu aku butuh kembali dan janganlah Engkau lupakan aku kepada ilmu itu wahai Tuhan seru sekalian alam" + }, + { + "title": "Doa Berpergian", + "arabic": "اَللّٰهُمَّ هَوِّنْ عَلَيْنَا سَفَرَنَا هَذَا وَاطْوِعَنَّابُعْدَهُ اَللّٰهُمَّ اَنْتَ الصَّاحِبُ فِى السَّفَرِوَالْخَلِيْفَةُفِى الْاَهْلِ", + "latin": "Alloohumma hawwin 'alainaa safaranaa hadzaa waatwi 'annaa bu'dahu. Alloohumma antashookhibu fiissafari walkholiifatu fiil ahli", + "translation": "Ya Allah, mudahkanlah kami berpergian ini, dan dekatkanlah kejauhannya. Ya Allah yang menemani dalam berpergian, dan Engkau pula yang melindungi keluarga" + }, + { + "title": "Doa Naik Kendaraan", + "arabic": "سُبْحَانَ الَّذِىْ سَخَّرَلَنَا هَذَا وَمَاكُنَّالَهُ مُقْرِنِيْنَ وَاِنَّآ اِلَى رَبِّنَا لَمُنْقَلِبُوْنَ", + "latin": "Subhaanalladzii sakkhara lanaa hadza wama kunna lahu muqriniin wa-inna ilaa rabbina lamunqalibuun.", + "translation": "Maha suci Allah yang telah menundukkan untuk kami (kendaraan) ini. padahal sebelumnya kami tidak mampu untuk menguasainya, dan hanya kepada-Mu lah kami akan kembali " + }, + { + "title": "Doa Naik Kapal", + "arabic": "بِسْمِ اللهِ مَجْرَهَا وَمُرْسَهَآاِنَّ رَبِّىْ لَغَفُوْرٌرَّحِيْمٌ", + "latin": "Bismillaahi majrahaa wa mursaahaa inna robbii laghofuurur rohiim", + "translation": "Dengan nama Allah yang menjalankan kendaraan ini berlayar dan berlabuh, sesungguhnya Tuhanku benar-benar Maha Pengampun lagi Maha Penyayang" + }, + { + "title": "Doa Ketika Sampai di Tempat Tujuan", + "arabic": "اَلْحَمْدُ ِللهِ الَّذِى سَلَمَنِى وَالَّذِى اَوَنِى وَالَّذِى جَمَعَ الشَّمْلَ بِ", + "latin": "Alhamdulillahil ladzi sallamani wal ladzi awani wal ladzi jama’asy syamla bi", + "translation": "Segala puji bagi Allah, yang telah menyelamatkan akau dan yang telah melindungiku dan yang mengumpulkanku dengan keluargaku" + }, + { + "title": "Doa Ketika Menuju Masjid", + "arabic": "اَللّٰهُمَّ اجْعَلْ فِىْ قَلْبِى نُوْرًا وَفِى لِسَانِىْ نُوْرًا وَفِىْ بَصَرِىْ نُوْرًا وَفِىْ سَمْعِىْ نُوْرًا وَعَنْ يَسَارِىْ نُوْرًا وَعَنْ يَمِيْنِىْ نُوْرًا وَفَوْقِىْ نُوْرًا وَتَحْتِىْ نُوْرًا وَاَمَامِىْ نُوْرًا وَخَلْفِىْ نُوْرًا وَاجْعَلْ لِّىْ نُوْرًا", + "latin": "Alloohummaj-'al fii qolbhii nuuroon wa fii lisaanii nuuroon wa fii bashorii nuuroon wa fii sam'ii nuuroon wa'an yamiinii nuuroon wa'an yasaarii nuuroon wa fauqii nuuroo wa tahtii nuuroo wa amaamii nuuroon wa khofii nuuroon waj-'al lii nuuroon", + "translation": "Ya Allah, jadikanlah dihatiku cahaya, pada lisanku cahaya dipandanganku cahaya, dalam pendengaranku cahaya, dari kananku cahaya, dari kiriku cahaya, dari atasku cahaya, dari bawahku cahaya, dari depanku cahaya, belakangku cahaya, dan jadikanlah untukku cahaya (H.R. Bukhari dan Muslim)" + }, + { + "title": "Doa Masuk Masjid", + "arabic": "اَللّٰهُمَّ افْتَحْ لِيْ اَبْوَابَ رَحْمَتِكَ", + "latin": "Allahummaf tahlii abwaaba rohmatik", + "translation": "Ya Allah, bukalah untukku pintu-pintu rahmat-Mu" + }, + { + "title": "Doa Keluar Masjid", + "arabic": "اَللّٰهُمَّ اِنِّى اَسْأَلُكَ مِنْ فَضْلِكَ", + "latin": "Allahumma innii asaluka min fadlik", + "translation": "Ya Allah, sesungguhnya aku memohon keutamaan dari-Mu" + }, + { + "title": "Doa Akan Membaca Al-Qur'an", + "arabic": "اَللّٰهُمَّ افْتَحْ عَلَىَّ حِكْمَتَكَ وَانْشُرْ عَلَىَّ رَحْمَتَكَ وَذَكِّرْنِىْ مَانَسِيْتُ يَاذَاالْجَلاَلِ وَاْلاِكْرَامِ", + "latin": "Alloohummaftah 'alayya hikmataka wansyur 'alayya rohmataka wa dzakkirnii maanasiitu yaa dzal jalaali wal ikhroomi", + "translation": "Ya Allah bukakanlah hikmahMu padaku, bentangkanlah rahmatMu padaku dan ingatkanlah aku terhadap apa yang aku lupa, wahai Dzat yang memiliki keagungan dan kemuliaan" + }, + { + "title": "Doa Setelah Membaca Al-Qur'an", + "arabic": "اَللّٰهُمَّ ارْحَمْنِىْ بِالْقُرْآنِ. وَاجْعَلْهُ لِىْ اِمَامًا وَنُوْرًا وَّهُدًى وَّرَحْمَةً. اَللّٰهُمَّ ذَكِّرْنِىْ مِنْهُ مَانَسِيْتُ وَعَلِّمْنِىْ مِنْهُ مَاجَهِلْتُ. وَارْزُقْنِىْ تِلاَ وَتَهُ آنَآءَ اللَّيْلِ وَاَطْرَافَ النَّهَارٍ. وَاجْعَلْهُ لِىْ حُجَّةً يَارَبَّ الْعَالَمِيْنَ", + "latin": "Allahummarhamnii bil qur'aani. waj'alhu lii imaaman wa nuuran wa hudan wa rohman. Allahumma dzakkirnii minhu maa nasiitu wa'allimnii minhu maa jahiltu. wazuqnii tilaa watahu aanaa-al laili wa athroofan nahaari. waj'alhu lii hujjatan yaa robbal 'aalamiina.", + "translation": "Ya Allah, rahmatilah aku dengan Al-Quran yang agung, jadikanlah ia bagiku ikutan cahaya petunjuk rahmat. Ya Allah, ingatkanlah apa yang telah aku lupa dan ajarkan kepadaku apa yang tidak aku ketahui darinya, anugerahkanlah padaku kesempatan membacanya pada sebagian malam dan siang, jadikanlah ia hujjah yang kuat bagiku, wahai Tuhan seru sekalian alam" + }, + { + "title": "Doa Niat Wudhu", + "arabic": "نَوَيْتُ الْوُضُوْءَ لِرَفْعِ الْحَدَثِ اْلاَصْغَرِ فَرْضًا لِلّٰهِ تَعَالَى", + "latin": "Nawaitul whudu-a lirof'il hadatsii ashghori fardhon lillaahi ta'aalaa", + "translation": "Saya niat berwudhu untuk menghilangkan hadast kecil fardu (wajib) karena Allah ta'ala" + }, + { + "title": "Doa Setelah Wudhu", + "arabic": "اَشْهَدُ اَنْ لاَّاِلَهَ اِلاَّاللهُ وَحْدَهُ لاَشَرِيْكَ لَهُ وَاَشْهَدُ اَنَّ مُحَمَّدًاعَبْدُهُ وَرَسُوْلُهُ. اَللّٰهُمَّ اجْعَلْنِىْ مِنَ التَّوَّابِيْنَ وَاجْعَلْنِىْ مِنَ الْمُتَطَهِّرِيْنَ، وَجْعَلْنِيْ مِنْ عِبَادِكَ الصَّالِحِيْنَ", + "latin": "Asyhadu allaa ilaaha illalloohu wahdahuu laa syariika lahu wa asyhadu anna muhammadan ‘abduhuuwa rosuuluhuu, alloohummaj’alnii minat tawwaabiina waj’alnii minal mutathohhiriina, waj'alnii min 'ibadikash shaalihiina", + "translation": "Aku bersaksi, tidak ada Tuhan selain Allah Yang Maha Esa, tidak ada sekutu bagi-Nya, dan aku mengaku bahwa Nabi Muhammad itu adalah hamba dan Utusan Allah. Ya Allah, jadikanlah aku dari golongan orang-orang yang bertaubat dan jadikanlah aku dari golongan orang-orang yang suci dan jadikanlah aku dari golongan hamba-hamba Mu yang shaleh" + }, + { + "title": "Doa akan Mandi", + "arabic": "اَللّٰهُمَّ اغْفِرْلِى ذَنْبِى وَوَسِّعْ لِى فِىْ دَارِىْ وَبَارِكْ لِىْ فِىْ رِزْقِىْ", + "latin": "Alloohummaghfirlii dzambii wa wassi'lii fii daarii wa baarik lii fii rizqii", + "translation": "Ya Allah ampunilah dosa kesalahanku dan berilah keluasaan di rumahku serta berkahilah pada rezekiku" + } + ] +} \ No newline at end of file diff --git a/assets/model/shalah_time.json b/assets/model/shalah_time.json new file mode 100644 index 0000000..6ac00d1 --- /dev/null +++ b/assets/model/shalah_time.json @@ -0,0 +1,2618 @@ +{ + "code": 200, + "status": "OK", + "data": [ + { + "timings": { + "Fajr": "04:59 (BST)", + "Sunrise": "06:36 (BST)", + "Dhuhr": "13:04 (BST)", + "Asr": "16:37 (BST)", + "Sunset": "19:34 (BST)", + "Maghrib": "19:34 (BST)", + "Isha": "21:10 (BST)", + "Imsak": "04:49 (BST)", + "Midnight": "01:05 (BST)", + "Firstthird": "23:15 (BST)", + "Lastthird": "02:55 (BST)" + }, + "date": { + "readable": "01 Apr 2017", + "timestamp": "1491033661", + "gregorian": { + "date": "01-04-2017", + "format": "DD-MM-YYYY", + "day": "01", + "weekday": { + "en": "Saturday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "04-07-1438", + "format": "DD-MM-YYYY", + "day": "04", + "weekday": { + "en": "Al Sabt", + "ar": "السبت" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:57 (BST)", + "Sunrise": "06:33 (BST)", + "Dhuhr": "13:04 (BST)", + "Asr": "16:38 (BST)", + "Sunset": "19:36 (BST)", + "Maghrib": "19:36 (BST)", + "Isha": "21:13 (BST)", + "Imsak": "04:47 (BST)", + "Midnight": "01:05 (BST)", + "Firstthird": "23:15 (BST)", + "Lastthird": "02:54 (BST)" + }, + "date": { + "readable": "02 Apr 2017", + "timestamp": "1491120061", + "gregorian": { + "date": "02-04-2017", + "format": "DD-MM-YYYY", + "day": "02", + "weekday": { + "en": "Sunday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "05-07-1438", + "format": "DD-MM-YYYY", + "day": "05", + "weekday": { + "en": "Al Ahad", + "ar": "الاحد" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:54 (BST)", + "Sunrise": "06:31 (BST)", + "Dhuhr": "13:04 (BST)", + "Asr": "16:39 (BST)", + "Sunset": "19:37 (BST)", + "Maghrib": "19:37 (BST)", + "Isha": "21:15 (BST)", + "Imsak": "04:44 (BST)", + "Midnight": "01:04 (BST)", + "Firstthird": "23:15 (BST)", + "Lastthird": "02:53 (BST)" + }, + "date": { + "readable": "03 Apr 2017", + "timestamp": "1491206461", + "gregorian": { + "date": "03-04-2017", + "format": "DD-MM-YYYY", + "day": "03", + "weekday": { + "en": "Monday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "06-07-1438", + "format": "DD-MM-YYYY", + "day": "06", + "weekday": { + "en": "Al Athnayn", + "ar": "الاثنين" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:51 (BST)", + "Sunrise": "06:29 (BST)", + "Dhuhr": "13:03 (BST)", + "Asr": "16:39 (BST)", + "Sunset": "19:39 (BST)", + "Maghrib": "19:39 (BST)", + "Isha": "21:17 (BST)", + "Imsak": "04:41 (BST)", + "Midnight": "01:04 (BST)", + "Firstthird": "23:16 (BST)", + "Lastthird": "02:52 (BST)" + }, + "date": { + "readable": "04 Apr 2017", + "timestamp": "1491292861", + "gregorian": { + "date": "04-04-2017", + "format": "DD-MM-YYYY", + "day": "04", + "weekday": { + "en": "Tuesday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "07-07-1438", + "format": "DD-MM-YYYY", + "day": "07", + "weekday": { + "en": "Al Thalaata", + "ar": "الثلاثاء" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:49 (BST)", + "Sunrise": "06:27 (BST)", + "Dhuhr": "13:03 (BST)", + "Asr": "16:40 (BST)", + "Sunset": "19:41 (BST)", + "Maghrib": "19:41 (BST)", + "Isha": "21:19 (BST)", + "Imsak": "04:39 (BST)", + "Midnight": "01:04 (BST)", + "Firstthird": "23:16 (BST)", + "Lastthird": "02:51 (BST)" + }, + "date": { + "readable": "05 Apr 2017", + "timestamp": "1491379261", + "gregorian": { + "date": "05-04-2017", + "format": "DD-MM-YYYY", + "day": "05", + "weekday": { + "en": "Wednesday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "08-07-1438", + "format": "DD-MM-YYYY", + "day": "08", + "weekday": { + "en": "Al Arba'a", + "ar": "الاربعاء" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:46 (BST)", + "Sunrise": "06:24 (BST)", + "Dhuhr": "13:03 (BST)", + "Asr": "16:41 (BST)", + "Sunset": "19:42 (BST)", + "Maghrib": "19:42 (BST)", + "Isha": "21:21 (BST)", + "Imsak": "04:36 (BST)", + "Midnight": "01:03 (BST)", + "Firstthird": "23:16 (BST)", + "Lastthird": "02:50 (BST)" + }, + "date": { + "readable": "06 Apr 2017", + "timestamp": "1491465661", + "gregorian": { + "date": "06-04-2017", + "format": "DD-MM-YYYY", + "day": "06", + "weekday": { + "en": "Thursday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "09-07-1438", + "format": "DD-MM-YYYY", + "day": "09", + "weekday": { + "en": "Al Khamees", + "ar": "الخميس" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:43 (BST)", + "Sunrise": "06:22 (BST)", + "Dhuhr": "13:03 (BST)", + "Asr": "16:42 (BST)", + "Sunset": "19:44 (BST)", + "Maghrib": "19:44 (BST)", + "Isha": "21:23 (BST)", + "Imsak": "04:33 (BST)", + "Midnight": "01:03 (BST)", + "Firstthird": "23:17 (BST)", + "Lastthird": "02:49 (BST)" + }, + "date": { + "readable": "07 Apr 2017", + "timestamp": "1491552061", + "gregorian": { + "date": "07-04-2017", + "format": "DD-MM-YYYY", + "day": "07", + "weekday": { + "en": "Friday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "10-07-1438", + "format": "DD-MM-YYYY", + "day": "10", + "weekday": { + "en": "Al Juma'a", + "ar": "الجمعة" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:41 (BST)", + "Sunrise": "06:20 (BST)", + "Dhuhr": "13:02 (BST)", + "Asr": "16:43 (BST)", + "Sunset": "19:46 (BST)", + "Maghrib": "19:46 (BST)", + "Isha": "21:25 (BST)", + "Imsak": "04:31 (BST)", + "Midnight": "01:03 (BST)", + "Firstthird": "23:17 (BST)", + "Lastthird": "02:48 (BST)" + }, + "date": { + "readable": "08 Apr 2017", + "timestamp": "1491638461", + "gregorian": { + "date": "08-04-2017", + "format": "DD-MM-YYYY", + "day": "08", + "weekday": { + "en": "Saturday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "11-07-1438", + "format": "DD-MM-YYYY", + "day": "11", + "weekday": { + "en": "Al Sabt", + "ar": "السبت" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:38 (BST)", + "Sunrise": "06:18 (BST)", + "Dhuhr": "13:02 (BST)", + "Asr": "16:44 (BST)", + "Sunset": "19:47 (BST)", + "Maghrib": "19:47 (BST)", + "Isha": "21:27 (BST)", + "Imsak": "04:28 (BST)", + "Midnight": "01:03 (BST)", + "Firstthird": "23:17 (BST)", + "Lastthird": "02:48 (BST)" + }, + "date": { + "readable": "09 Apr 2017", + "timestamp": "1491724861", + "gregorian": { + "date": "09-04-2017", + "format": "DD-MM-YYYY", + "day": "09", + "weekday": { + "en": "Sunday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "12-07-1438", + "format": "DD-MM-YYYY", + "day": "12", + "weekday": { + "en": "Al Ahad", + "ar": "الاحد" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:35 (BST)", + "Sunrise": "06:15 (BST)", + "Dhuhr": "13:02 (BST)", + "Asr": "16:45 (BST)", + "Sunset": "19:49 (BST)", + "Maghrib": "19:49 (BST)", + "Isha": "21:30 (BST)", + "Imsak": "04:25 (BST)", + "Midnight": "01:02 (BST)", + "Firstthird": "23:18 (BST)", + "Lastthird": "02:47 (BST)" + }, + "date": { + "readable": "10 Apr 2017", + "timestamp": "1491811261", + "gregorian": { + "date": "10-04-2017", + "format": "DD-MM-YYYY", + "day": "10", + "weekday": { + "en": "Monday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "13-07-1438", + "format": "DD-MM-YYYY", + "day": "13", + "weekday": { + "en": "Al Athnayn", + "ar": "الاثنين" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:32 (BST)", + "Sunrise": "06:13 (BST)", + "Dhuhr": "13:02 (BST)", + "Asr": "16:45 (BST)", + "Sunset": "19:51 (BST)", + "Maghrib": "19:51 (BST)", + "Isha": "21:32 (BST)", + "Imsak": "04:22 (BST)", + "Midnight": "01:02 (BST)", + "Firstthird": "23:18 (BST)", + "Lastthird": "02:46 (BST)" + }, + "date": { + "readable": "11 Apr 2017", + "timestamp": "1491897661", + "gregorian": { + "date": "11-04-2017", + "format": "DD-MM-YYYY", + "day": "11", + "weekday": { + "en": "Tuesday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "14-07-1438", + "format": "DD-MM-YYYY", + "day": "14", + "weekday": { + "en": "Al Thalaata", + "ar": "الثلاثاء" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:30 (BST)", + "Sunrise": "06:11 (BST)", + "Dhuhr": "13:01 (BST)", + "Asr": "16:46 (BST)", + "Sunset": "19:52 (BST)", + "Maghrib": "19:52 (BST)", + "Isha": "21:34 (BST)", + "Imsak": "04:20 (BST)", + "Midnight": "01:02 (BST)", + "Firstthird": "23:19 (BST)", + "Lastthird": "02:45 (BST)" + }, + "date": { + "readable": "12 Apr 2017", + "timestamp": "1491984061", + "gregorian": { + "date": "12-04-2017", + "format": "DD-MM-YYYY", + "day": "12", + "weekday": { + "en": "Wednesday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "15-07-1438", + "format": "DD-MM-YYYY", + "day": "15", + "weekday": { + "en": "Al Arba'a", + "ar": "الاربعاء" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:27 (BST)", + "Sunrise": "06:09 (BST)", + "Dhuhr": "13:01 (BST)", + "Asr": "16:47 (BST)", + "Sunset": "19:54 (BST)", + "Maghrib": "19:54 (BST)", + "Isha": "21:36 (BST)", + "Imsak": "04:17 (BST)", + "Midnight": "01:01 (BST)", + "Firstthird": "23:19 (BST)", + "Lastthird": "02:44 (BST)" + }, + "date": { + "readable": "13 Apr 2017", + "timestamp": "1492070461", + "gregorian": { + "date": "13-04-2017", + "format": "DD-MM-YYYY", + "day": "13", + "weekday": { + "en": "Thursday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "16-07-1438", + "format": "DD-MM-YYYY", + "day": "16", + "weekday": { + "en": "Al Khamees", + "ar": "الخميس" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:24 (BST)", + "Sunrise": "06:07 (BST)", + "Dhuhr": "13:01 (BST)", + "Asr": "16:48 (BST)", + "Sunset": "19:56 (BST)", + "Maghrib": "19:56 (BST)", + "Isha": "21:39 (BST)", + "Imsak": "04:14 (BST)", + "Midnight": "01:01 (BST)", + "Firstthird": "23:19 (BST)", + "Lastthird": "02:43 (BST)" + }, + "date": { + "readable": "14 Apr 2017", + "timestamp": "1492156861", + "gregorian": { + "date": "14-04-2017", + "format": "DD-MM-YYYY", + "day": "14", + "weekday": { + "en": "Friday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "17-07-1438", + "format": "DD-MM-YYYY", + "day": "17", + "weekday": { + "en": "Al Juma'a", + "ar": "الجمعة" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:21 (BST)", + "Sunrise": "06:05 (BST)", + "Dhuhr": "13:00 (BST)", + "Asr": "16:49 (BST)", + "Sunset": "19:57 (BST)", + "Maghrib": "19:57 (BST)", + "Isha": "21:41 (BST)", + "Imsak": "04:11 (BST)", + "Midnight": "01:01 (BST)", + "Firstthird": "23:20 (BST)", + "Lastthird": "02:42 (BST)" + }, + "date": { + "readable": "15 Apr 2017", + "timestamp": "1492243261", + "gregorian": { + "date": "15-04-2017", + "format": "DD-MM-YYYY", + "day": "15", + "weekday": { + "en": "Saturday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "18-07-1438", + "format": "DD-MM-YYYY", + "day": "18", + "weekday": { + "en": "Al Sabt", + "ar": "السبت" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:19 (BST)", + "Sunrise": "06:02 (BST)", + "Dhuhr": "13:00 (BST)", + "Asr": "16:49 (BST)", + "Sunset": "19:59 (BST)", + "Maghrib": "19:59 (BST)", + "Isha": "21:43 (BST)", + "Imsak": "04:09 (BST)", + "Midnight": "01:01 (BST)", + "Firstthird": "23:20 (BST)", + "Lastthird": "02:41 (BST)" + }, + "date": { + "readable": "16 Apr 2017", + "timestamp": "1492329661", + "gregorian": { + "date": "16-04-2017", + "format": "DD-MM-YYYY", + "day": "16", + "weekday": { + "en": "Sunday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "19-07-1438", + "format": "DD-MM-YYYY", + "day": "19", + "weekday": { + "en": "Al Ahad", + "ar": "الاحد" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:16 (BST)", + "Sunrise": "06:00 (BST)", + "Dhuhr": "13:00 (BST)", + "Asr": "16:50 (BST)", + "Sunset": "20:01 (BST)", + "Maghrib": "20:01 (BST)", + "Isha": "21:46 (BST)", + "Imsak": "04:06 (BST)", + "Midnight": "01:01 (BST)", + "Firstthird": "23:21 (BST)", + "Lastthird": "02:40 (BST)" + }, + "date": { + "readable": "17 Apr 2017", + "timestamp": "1492416061", + "gregorian": { + "date": "17-04-2017", + "format": "DD-MM-YYYY", + "day": "17", + "weekday": { + "en": "Monday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "20-07-1438", + "format": "DD-MM-YYYY", + "day": "20", + "weekday": { + "en": "Al Athnayn", + "ar": "الاثنين" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:13 (BST)", + "Sunrise": "05:58 (BST)", + "Dhuhr": "13:00 (BST)", + "Asr": "16:51 (BST)", + "Sunset": "20:02 (BST)", + "Maghrib": "20:02 (BST)", + "Isha": "21:48 (BST)", + "Imsak": "04:03 (BST)", + "Midnight": "01:00 (BST)", + "Firstthird": "23:21 (BST)", + "Lastthird": "02:40 (BST)" + }, + "date": { + "readable": "18 Apr 2017", + "timestamp": "1492502461", + "gregorian": { + "date": "18-04-2017", + "format": "DD-MM-YYYY", + "day": "18", + "weekday": { + "en": "Tuesday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "21-07-1438", + "format": "DD-MM-YYYY", + "day": "21", + "weekday": { + "en": "Al Thalaata", + "ar": "الثلاثاء" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:10 (BST)", + "Sunrise": "05:56 (BST)", + "Dhuhr": "13:00 (BST)", + "Asr": "16:52 (BST)", + "Sunset": "20:04 (BST)", + "Maghrib": "20:04 (BST)", + "Isha": "21:50 (BST)", + "Imsak": "04:00 (BST)", + "Midnight": "01:00 (BST)", + "Firstthird": "23:21 (BST)", + "Lastthird": "02:39 (BST)" + }, + "date": { + "readable": "19 Apr 2017", + "timestamp": "1492588861", + "gregorian": { + "date": "19-04-2017", + "format": "DD-MM-YYYY", + "day": "19", + "weekday": { + "en": "Wednesday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "22-07-1438", + "format": "DD-MM-YYYY", + "day": "22", + "weekday": { + "en": "Al Arba'a", + "ar": "الاربعاء" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:07 (BST)", + "Sunrise": "05:54 (BST)", + "Dhuhr": "12:59 (BST)", + "Asr": "16:52 (BST)", + "Sunset": "20:06 (BST)", + "Maghrib": "20:06 (BST)", + "Isha": "21:53 (BST)", + "Imsak": "03:57 (BST)", + "Midnight": "01:00 (BST)", + "Firstthird": "23:22 (BST)", + "Lastthird": "02:38 (BST)" + }, + "date": { + "readable": "20 Apr 2017", + "timestamp": "1492675261", + "gregorian": { + "date": "20-04-2017", + "format": "DD-MM-YYYY", + "day": "20", + "weekday": { + "en": "Thursday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "23-07-1438", + "format": "DD-MM-YYYY", + "day": "23", + "weekday": { + "en": "Al Khamees", + "ar": "الخميس" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:05 (BST)", + "Sunrise": "05:52 (BST)", + "Dhuhr": "12:59 (BST)", + "Asr": "16:53 (BST)", + "Sunset": "20:07 (BST)", + "Maghrib": "20:07 (BST)", + "Isha": "21:55 (BST)", + "Imsak": "03:55 (BST)", + "Midnight": "01:00 (BST)", + "Firstthird": "23:22 (BST)", + "Lastthird": "02:37 (BST)" + }, + "date": { + "readable": "21 Apr 2017", + "timestamp": "1492761661", + "gregorian": { + "date": "21-04-2017", + "format": "DD-MM-YYYY", + "day": "21", + "weekday": { + "en": "Friday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "24-07-1438", + "format": "DD-MM-YYYY", + "day": "24", + "weekday": { + "en": "Al Juma'a", + "ar": "الجمعة" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "04:02 (BST)", + "Sunrise": "05:50 (BST)", + "Dhuhr": "12:59 (BST)", + "Asr": "16:54 (BST)", + "Sunset": "20:09 (BST)", + "Maghrib": "20:09 (BST)", + "Isha": "21:58 (BST)", + "Imsak": "03:52 (BST)", + "Midnight": "00:59 (BST)", + "Firstthird": "23:23 (BST)", + "Lastthird": "02:36 (BST)" + }, + "date": { + "readable": "22 Apr 2017", + "timestamp": "1492848061", + "gregorian": { + "date": "22-04-2017", + "format": "DD-MM-YYYY", + "day": "22", + "weekday": { + "en": "Saturday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "25-07-1438", + "format": "DD-MM-YYYY", + "day": "25", + "weekday": { + "en": "Al Sabt", + "ar": "السبت" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "03:59 (BST)", + "Sunrise": "05:48 (BST)", + "Dhuhr": "12:59 (BST)", + "Asr": "16:55 (BST)", + "Sunset": "20:11 (BST)", + "Maghrib": "20:11 (BST)", + "Isha": "22:00 (BST)", + "Imsak": "03:49 (BST)", + "Midnight": "00:59 (BST)", + "Firstthird": "23:23 (BST)", + "Lastthird": "02:35 (BST)" + }, + "date": { + "readable": "23 Apr 2017", + "timestamp": "1492934461", + "gregorian": { + "date": "23-04-2017", + "format": "DD-MM-YYYY", + "day": "23", + "weekday": { + "en": "Sunday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "26-07-1438", + "format": "DD-MM-YYYY", + "day": "26", + "weekday": { + "en": "Al Ahad", + "ar": "الاحد" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "03:56 (BST)", + "Sunrise": "05:46 (BST)", + "Dhuhr": "12:59 (BST)", + "Asr": "16:55 (BST)", + "Sunset": "20:12 (BST)", + "Maghrib": "20:12 (BST)", + "Isha": "22:03 (BST)", + "Imsak": "03:46 (BST)", + "Midnight": "00:59 (BST)", + "Firstthird": "23:24 (BST)", + "Lastthird": "02:35 (BST)" + }, + "date": { + "readable": "24 Apr 2017", + "timestamp": "1493020861", + "gregorian": { + "date": "24-04-2017", + "format": "DD-MM-YYYY", + "day": "24", + "weekday": { + "en": "Monday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "27-07-1438", + "format": "DD-MM-YYYY", + "day": "27", + "weekday": { + "en": "Al Athnayn", + "ar": "الاثنين" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [ + "Lailat-ul-Miraj" + ] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "03:53 (BST)", + "Sunrise": "05:44 (BST)", + "Dhuhr": "12:58 (BST)", + "Asr": "16:56 (BST)", + "Sunset": "20:14 (BST)", + "Maghrib": "20:14 (BST)", + "Isha": "22:05 (BST)", + "Imsak": "03:43 (BST)", + "Midnight": "00:59 (BST)", + "Firstthird": "23:24 (BST)", + "Lastthird": "02:34 (BST)" + }, + "date": { + "readable": "25 Apr 2017", + "timestamp": "1493107261", + "gregorian": { + "date": "25-04-2017", + "format": "DD-MM-YYYY", + "day": "25", + "weekday": { + "en": "Tuesday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "28-07-1438", + "format": "DD-MM-YYYY", + "day": "28", + "weekday": { + "en": "Al Thalaata", + "ar": "الثلاثاء" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "03:50 (BST)", + "Sunrise": "05:42 (BST)", + "Dhuhr": "12:58 (BST)", + "Asr": "16:57 (BST)", + "Sunset": "20:16 (BST)", + "Maghrib": "20:16 (BST)", + "Isha": "22:08 (BST)", + "Imsak": "03:40 (BST)", + "Midnight": "00:59 (BST)", + "Firstthird": "23:24 (BST)", + "Lastthird": "02:33 (BST)" + }, + "date": { + "readable": "26 Apr 2017", + "timestamp": "1493193661", + "gregorian": { + "date": "26-04-2017", + "format": "DD-MM-YYYY", + "day": "26", + "weekday": { + "en": "Wednesday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "29-07-1438", + "format": "DD-MM-YYYY", + "day": "29", + "weekday": { + "en": "Al Arba'a", + "ar": "الاربعاء" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "03:47 (BST)", + "Sunrise": "05:40 (BST)", + "Dhuhr": "12:58 (BST)", + "Asr": "16:57 (BST)", + "Sunset": "20:17 (BST)", + "Maghrib": "20:17 (BST)", + "Isha": "22:10 (BST)", + "Imsak": "03:37 (BST)", + "Midnight": "00:59 (BST)", + "Firstthird": "23:25 (BST)", + "Lastthird": "02:32 (BST)" + }, + "date": { + "readable": "27 Apr 2017", + "timestamp": "1493280061", + "gregorian": { + "date": "27-04-2017", + "format": "DD-MM-YYYY", + "day": "27", + "weekday": { + "en": "Thursday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "30-07-1438", + "format": "DD-MM-YYYY", + "day": "30", + "weekday": { + "en": "Al Khamees", + "ar": "الخميس" + }, + "month": { + "number": 7, + "en": "Rajab", + "ar": "رَجَب" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "03:45 (BST)", + "Sunrise": "05:38 (BST)", + "Dhuhr": "12:58 (BST)", + "Asr": "16:58 (BST)", + "Sunset": "20:19 (BST)", + "Maghrib": "20:19 (BST)", + "Isha": "22:13 (BST)", + "Imsak": "03:35 (BST)", + "Midnight": "00:58 (BST)", + "Firstthird": "23:25 (BST)", + "Lastthird": "02:32 (BST)" + }, + "date": { + "readable": "28 Apr 2017", + "timestamp": "1493366461", + "gregorian": { + "date": "28-04-2017", + "format": "DD-MM-YYYY", + "day": "28", + "weekday": { + "en": "Friday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "01-08-1438", + "format": "DD-MM-YYYY", + "day": "01", + "weekday": { + "en": "Al Juma'a", + "ar": "الجمعة" + }, + "month": { + "number": 8, + "en": "Shaʿbān", + "ar": "شَعْبان" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "03:42 (BST)", + "Sunrise": "05:36 (BST)", + "Dhuhr": "12:58 (BST)", + "Asr": "16:59 (BST)", + "Sunset": "20:21 (BST)", + "Maghrib": "20:21 (BST)", + "Isha": "22:15 (BST)", + "Imsak": "03:32 (BST)", + "Midnight": "00:58 (BST)", + "Firstthird": "23:26 (BST)", + "Lastthird": "02:31 (BST)" + }, + "date": { + "readable": "29 Apr 2017", + "timestamp": "1493452861", + "gregorian": { + "date": "29-04-2017", + "format": "DD-MM-YYYY", + "day": "29", + "weekday": { + "en": "Saturday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "02-08-1438", + "format": "DD-MM-YYYY", + "day": "02", + "weekday": { + "en": "Al Sabt", + "ar": "السبت" + }, + "month": { + "number": 8, + "en": "Shaʿbān", + "ar": "شَعْبان" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + }, + { + "timings": { + "Fajr": "03:39 (BST)", + "Sunrise": "05:34 (BST)", + "Dhuhr": "12:58 (BST)", + "Asr": "16:59 (BST)", + "Sunset": "20:22 (BST)", + "Maghrib": "20:22 (BST)", + "Isha": "22:18 (BST)", + "Imsak": "03:29 (BST)", + "Midnight": "00:58 (BST)", + "Firstthird": "23:26 (BST)", + "Lastthird": "02:30 (BST)" + }, + "date": { + "readable": "30 Apr 2017", + "timestamp": "1493539261", + "gregorian": { + "date": "30-04-2017", + "format": "DD-MM-YYYY", + "day": "30", + "weekday": { + "en": "Sunday" + }, + "month": { + "number": 4, + "en": "April" + }, + "year": "2017", + "designation": { + "abbreviated": "AD", + "expanded": "Anno Domini" + } + }, + "hijri": { + "date": "03-08-1438", + "format": "DD-MM-YYYY", + "day": "03", + "weekday": { + "en": "Al Ahad", + "ar": "الاحد" + }, + "month": { + "number": 8, + "en": "Shaʿbān", + "ar": "شَعْبان" + }, + "year": "1438", + "designation": { + "abbreviated": "AH", + "expanded": "Anno Hegirae" + }, + "holidays": [] + } + }, + "meta": { + "latitude": 51.508515, + "longitude": -0.1254872, + "timezone": "Europe/London", + "method": { + "id": 2, + "name": "Islamic Society of North America (ISNA)", + "params": { + "Fajr": 15, + "Isha": 15 + }, + "location": { + "latitude": 39.70421229999999, + "longitude": -86.39943869999999 + } + }, + "latitudeAdjustmentMethod": "ANGLE_BASED", + "midnightMode": "STANDARD", + "school": "STANDARD", + "offset": { + "Imsak": 0, + "Fajr": 0, + "Sunrise": 0, + "Dhuhr": 0, + "Asr": 0, + "Maghrib": 0, + "Sunset": 0, + "Isha": 0, + "Midnight": 0 + } + } + } + ] +} \ No newline at end of file diff --git a/coverage/lcov.info b/coverage/lcov.info new file mode 100644 index 0000000..dfc1347 --- /dev/null +++ b/coverage/lcov.info @@ -0,0 +1,985 @@ +SF:lib\app\data\model\shalat_time_response.dart +DA:8,2 +DA:14,2 +DA:15,2 +DA:16,2 +DA:17,2 +DA:18,2 +DA:19,0 +DA:20,10 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:30,2 +DA:31,2 +DA:32,4 +DA:33,4 +DA:34,4 +DA:35,4 +DA:36,4 +DA:37,4 +DA:38,4 +DA:39,4 +DA:40,4 +DA:41,4 +DA:42,4 +DA:43,6 +DA:44,4 +DA:45,4 +DA:46,4 +DA:47,4 +DA:48,4 +DA:49,4 +DA:50,4 +DA:51,6 +DA:52,6 +DA:53,4 +DA:54,2 +DA:55,0 +DA:64,2 +DA:70,4 +DA:72,6 +DA:73,6 +DA:74,6 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:90,2 +DA:97,4 +DA:98,2 +DA:99,2 +DA:100,2 +DA:102,4 +DA:103,6 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:123,2 +DA:133,4 +DA:134,2 +DA:135,2 +DA:136,2 +DA:137,2 +DA:139,4 +DA:140,2 +DA:142,4 +DA:143,2 +DA:144,2 +DA:146,4 +DA:149,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:156,0 +DA:164,2 +DA:169,4 +DA:170,2 +DA:171,2 +DA:174,0 +DA:175,0 +DA:176,0 +DA:184,2 +DA:189,4 +DA:190,2 +DA:191,2 +DA:194,0 +DA:195,0 +DA:196,0 +DA:203,2 +DA:207,2 +DA:208,2 +DA:209,2 +DA:212,0 +DA:213,0 +DA:227,2 +DA:238,4 +DA:239,2 +DA:240,2 +DA:241,2 +DA:242,2 +DA:244,4 +DA:246,6 +DA:247,2 +DA:248,2 +DA:250,4 +DA:251,2 +DA:252,0 +DA:253,8 +DA:256,0 +DA:257,0 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:263,0 +DA:265,0 +DA:274,2 +DA:280,4 +DA:281,2 +DA:282,2 +DA:283,2 +DA:286,0 +DA:287,0 +DA:288,0 +DA:289,0 +DA:297,2 +DA:302,4 +DA:303,2 +DA:304,2 +DA:307,0 +DA:308,0 +DA:309,0 +DA:323,2 +DA:334,4 +DA:335,4 +DA:336,4 +DA:337,2 +DA:338,6 +DA:339,2 +DA:340,2 +DA:341,2 +DA:342,4 +DA:343,6 +DA:346,0 +DA:347,0 +DA:348,0 +DA:349,0 +DA:350,0 +DA:351,0 +DA:352,0 +DA:353,0 +DA:355,0 +DA:364,2 +DA:370,4 +DA:371,2 +DA:372,2 +DA:373,6 +DA:376,0 +DA:377,0 +DA:378,0 +DA:379,0 +DA:386,2 +DA:390,4 +DA:391,2 +DA:394,0 +DA:395,0 +DA:412,2 +DA:426,4 +DA:427,2 +DA:428,2 +DA:429,2 +DA:430,2 +DA:431,2 +DA:432,2 +DA:433,2 +DA:434,2 +DA:435,2 +DA:436,2 +DA:437,2 +DA:440,0 +DA:441,0 +DA:442,0 +DA:443,0 +DA:444,0 +DA:445,0 +DA:446,0 +DA:447,0 +DA:448,0 +DA:449,0 +DA:450,0 +DA:451,0 +LF:199 +LH:122 +end_of_record +SF:lib\app\data\providers\database_helper.dart +DA:9,0 +DA:13,0 +DA:17,0 +DA:18,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +LF:44 +LH:0 +end_of_record +SF:lib\app\data\providers\shalat_local_data_source.dart +DA:18,1 +DA:20,1 +DA:22,2 +DA:23,1 +DA:24,4 +DA:25,2 +DA:28,1 +DA:31,2 +DA:34,2 +DA:38,1 +DA:40,2 +DA:41,2 +LF:12 +LH:12 +end_of_record +SF:lib\app\domain\entities\shalat.dart +DA:11,2 +DA:25,0 +DA:26,0 +LF:3 +LH:1 +end_of_record +SF:lib\core\extensions\string_extension.dart +DA:3,1 +DA:7,2 +DA:8,2 +DA:9,2 +DA:10,1 +DA:11,2 +DA:12,1 +LF:7 +LH:7 +end_of_record +SF:lib\core\utils\exception.dart +DA:4,2 +DA:10,4 +LF:2 +LH:2 +end_of_record +SF:lib\core\utils\logger.dart +DA:21,3 +DA:22,15 +DA:26,0 +DA:27,0 +DA:31,0 +DA:32,0 +DA:36,0 +DA:37,0 +DA:41,0 +DA:42,0 +LF:10 +LH:2 +end_of_record +SF:lib\core\values\constant.dart +DA:3,0 +DA:15,0 +DA:37,0 +LF:3 +LH:0 +end_of_record +SF:lib\app\data\model\asmaul_husna_response.dart +DA:10,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,4 +DA:20,0 +DA:22,0 +DA:25,2 +DA:26,3 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:42,1 +DA:50,1 +DA:51,1 +DA:52,1 +DA:53,1 +DA:54,1 +DA:55,1 +DA:56,1 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +LF:29 +LH:21 +end_of_record +SF:lib\app\data\model\daily_pray_response.dart +DA:10,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:20,0 +DA:22,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:38,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +LF:24 +LH:0 +end_of_record +SF:lib\app\data\model\list_surah_response.dart +DA:9,0 +DA:11,2 +DA:12,4 +DA:13,4 +DA:14,4 +DA:15,2 +DA:16,4 +DA:17,6 +DA:18,6 +DA:23,2 +DA:24,2 +DA:25,4 +DA:26,2 +DA:27,2 +DA:28,2 +DA:29,4 +DA:30,4 +DA:31,4 +DA:32,6 +DA:33,4 +DA:34,6 +DA:35,2 +DA:36,2 +DA:37,4 +DA:38,4 +DA:39,6 +DA:40,4 +DA:41,2 +DA:42,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:65,0 +DA:73,2 +DA:74,4 +DA:75,4 +DA:76,4 +DA:77,8 +DA:78,4 +DA:79,4 +DA:81,8 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:92,0 +DA:93,0 +DA:95,0 +DA:96,0 +DA:108,0 +DA:110,2 +DA:111,4 +DA:112,4 +DA:113,4 +DA:114,4 +DA:116,4 +DA:117,4 +DA:121,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:128,0 +DA:129,0 +DA:139,0 +DA:141,2 +DA:142,4 +DA:143,4 +DA:146,0 +DA:147,0 +DA:148,0 +DA:149,0 +DA:159,0 +DA:161,2 +DA:162,4 +DA:163,4 +DA:164,4 +DA:167,0 +DA:168,0 +DA:169,0 +DA:170,0 +DA:171,0 +DA:179,0 +DA:181,2 +DA:182,4 +DA:185,0 +DA:186,0 +DA:187,0 +LF:96 +LH:51 +end_of_record +SF:lib\app\data\model\surah_detail_response.dart +DA:8,0 +DA:9,0 +DA:11,0 +DA:12,0 +DA:20,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,3 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:41,1 +DA:42,2 +DA:44,3 +DA:45,2 +DA:46,2 +DA:47,3 +DA:48,3 +DA:49,4 +DA:50,4 +DA:51,3 +DA:52,3 +DA:53,2 +DA:54,2 +DA:56,2 +DA:57,2 +DA:59,2 +DA:60,2 +DA:61,2 +DA:62,2 +DA:63,2 +DA:64,3 +DA:65,1 +DA:66,0 +DA:80,1 +DA:91,2 +DA:92,1 +DA:93,1 +DA:94,1 +DA:95,3 +DA:96,1 +DA:98,2 +DA:100,3 +DA:101,1 +DA:103,0 +DA:104,1 +DA:105,0 +DA:106,5 +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:129,1 +DA:136,2 +DA:137,1 +DA:138,1 +DA:139,1 +DA:141,2 +DA:142,1 +DA:144,2 +DA:147,0 +DA:148,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:159,1 +DA:164,2 +DA:165,1 +DA:166,1 +DA:169,0 +DA:170,0 +DA:171,0 +DA:180,0 +DA:186,0 +DA:187,0 +DA:188,0 +DA:190,0 +DA:191,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:197,0 +DA:205,1 +DA:210,2 +DA:211,1 +DA:212,1 +DA:213,0 +DA:214,4 +DA:217,0 +DA:218,0 +DA:219,0 +DA:220,0 +DA:221,0 +DA:229,1 +DA:234,2 +DA:235,1 +DA:236,1 +DA:238,2 +DA:241,0 +DA:242,0 +DA:243,0 +DA:250,1 +DA:254,1 +DA:255,1 +DA:256,1 +DA:259,0 +DA:260,0 +DA:269,1 +DA:275,2 +DA:276,1 +DA:277,1 +DA:278,1 +DA:281,0 +DA:282,0 +DA:283,0 +DA:284,0 +DA:291,1 +DA:295,2 +DA:296,1 +DA:299,0 +DA:300,0 +DA:312,1 +DA:321,2 +DA:322,3 +DA:323,3 +DA:324,3 +DA:325,1 +DA:327,2 +DA:328,3 +DA:329,1 +DA:331,2 +DA:334,0 +DA:335,0 +DA:336,0 +DA:337,0 +DA:338,0 +DA:339,0 +DA:340,0 +DA:352,1 +DA:361,2 +DA:362,1 +DA:363,1 +DA:364,1 +DA:365,1 +DA:366,1 +DA:367,3 +DA:370,0 +DA:371,0 +DA:372,0 +DA:373,0 +DA:374,0 +DA:375,0 +DA:376,0 +DA:384,1 +DA:389,2 +DA:390,1 +DA:391,1 +DA:394,0 +DA:395,0 +DA:396,0 +DA:404,1 +DA:409,2 +DA:410,1 +DA:411,1 +DA:414,0 +DA:415,0 +DA:416,0 +DA:423,1 +DA:427,2 +DA:428,3 +DA:431,0 +DA:432,0 +DA:440,1 +DA:445,2 +DA:446,1 +DA:447,1 +DA:450,0 +DA:451,0 +DA:452,0 +LF:191 +LH:108 +end_of_record +SF:lib\app\data\providers\shalat_remote_data_source.dart +DA:16,0 +DA:18,0 +DA:19,0 +DA:22,0 +DA:25,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:31,0 +DA:34,0 +LF:10 +LH:0 +end_of_record +SF:lib\app\data\providers\surah_local_data_source.dart +DA:25,1 +DA:27,1 +DA:29,2 +DA:32,0 +DA:36,0 +DA:37,0 +DA:40,0 +DA:44,0 +DA:48,0 +DA:49,0 +DA:52,0 +DA:56,1 +DA:58,2 +DA:61,1 +DA:64,2 +DA:67,2 +DA:71,1 +DA:74,2 +DA:77,2 +DA:81,1 +DA:83,2 +LF:21 +LH:13 +end_of_record +SF:lib\app\data\providers\surah_remote_data_source.dart +DA:17,0 +DA:19,0 +DA:22,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:29,0 +DA:30,0 +DA:33,0 +DA:34,0 +DA:38,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:46,0 +DA:49,0 +LF:17 +LH:0 +end_of_record +SF:lib\app\domain\entities\asmaul_husna.dart +DA:7,1 +LF:1 +LH:1 +end_of_record +SF:lib\app\domain\entities\daily_pray.dart +DA:6,0 +LF:1 +LH:0 +end_of_record +SF:lib\app\domain\entities\surah.dart +DA:10,2 +DA:20,0 +DA:21,0 +LF:3 +LH:1 +end_of_record +SF:lib\app\domain\entities\surah_detail.dart +DA:11,1 +DA:31,0 +DA:32,0 +DA:33,0 +LF:4 +LH:1 +end_of_record +SF:lib\core\utils\failure.dart +DA:6,4 +DA:8,2 +DA:9,4 +DA:13,6 +DA:17,6 +DA:21,6 +LF:6 +LH:6 +end_of_record +SF:lib\core\utils\error_handler.dart +DA:4,0 +DA:5,0 +DA:6,0 +DA:7,0 +DA:8,0 +DA:9,0 +DA:11,0 +DA:13,0 +DA:15,0 +DA:17,0 +DA:19,0 +DA:21,0 +DA:23,0 +DA:25,0 +LF:14 +LH:0 +end_of_record +SF:lib\app\modules\tasbih\controllers\tasbih_controller.dart +DA:8,1 +DA:9,2 +DA:10,4 +DA:13,6 +DA:16,2 +DA:17,4 +DA:18,6 +DA:19,4 +DA:23,6 +DA:25,2 +DA:27,2 +DA:28,8 +DA:31,0 +DA:33,0 +DA:34,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:41,0 +LF:19 +LH:12 +end_of_record +SF:lib\app\modules\tasbih\views\tasbih_view.dart +DA:8,1 +DA:10,1 +DA:12,1 +DA:13,1 +DA:21,1 +DA:22,0 +DA:30,1 +DA:31,1 +DA:32,1 +DA:34,1 +DA:35,1 +DA:38,1 +DA:39,6 +DA:40,4 +DA:42,5 +DA:46,1 +DA:50,1 +DA:52,1 +DA:65,1 +DA:66,1 +DA:68,3 +DA:71,1 +DA:73,1 +DA:75,1 +DA:76,1 +DA:81,2 +DA:83,3 +DA:85,1 +DA:89,1 +DA:90,2 +DA:91,2 +DA:93,1 +DA:94,1 +DA:96,1 +DA:98,1 +DA:99,1 +DA:111,1 +DA:113,1 +DA:117,1 +DA:119,1 +DA:121,1 +DA:122,1 +DA:123,1 +DA:124,0 +DA:125,0 +DA:127,3 +DA:129,1 +DA:130,3 +DA:133,1 +DA:149,1 +DA:150,1 +DA:151,0 +DA:152,0 +DA:154,3 +DA:156,1 +DA:157,3 +DA:160,1 +DA:176,1 +DA:177,1 +DA:178,0 +DA:179,0 +DA:181,3 +DA:183,1 +DA:184,3 +DA:187,1 +DA:219,1 +DA:220,1 +DA:223,2 +DA:224,1 +DA:227,1 +DA:229,1 +DA:230,3 +DA:232,1 +DA:233,1 +DA:234,1 +DA:243,1 +DA:245,1 +DA:246,0 +DA:247,0 +DA:249,1 +DA:250,1 +DA:260,1 +DA:262,1 +DA:263,0 +DA:264,0 +DA:266,1 +DA:267,1 +LF:87 +LH:76 +end_of_record +SF:lib\core\theme\colors.dart +DA:4,0 +LF:1 +LH:0 +end_of_record +SF:lib\app\data\repository\shalat_repository_impl.dart +DA:19,1 +DA:25,1 +DA:28,2 +DA:30,1 +DA:31,2 +DA:32,1 +DA:34,1 +DA:36,1 +DA:37,3 +DA:38,1 +DA:39,3 +DA:40,1 +DA:41,3 +DA:43,0 +DA:48,1 +DA:49,2 +DA:50,1 +DA:51,1 +DA:52,2 +DA:53,1 +LF:20 +LH:19 +end_of_record +SF:lib\app\data\repository\surah_repository_impl.dart +DA:21,1 +DA:26,0 +DA:29,0 +DA:30,0 +DA:32,0 +DA:36,1 +DA:39,2 +DA:40,2 +DA:41,1 +DA:42,3 +DA:44,0 +DA:48,1 +DA:49,2 +DA:50,1 +DA:51,1 +DA:52,2 +DA:53,1 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:64,0 +DA:65,0 +DA:71,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:89,0 +DA:106,1 +DA:109,2 +DA:110,1 +DA:111,1 +DA:112,2 +DA:113,1 +DA:115,1 +DA:116,1 +DA:117,3 +DA:118,1 +DA:119,3 +DA:120,1 +DA:121,3 +DA:123,0 +DA:139,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:147,0 +LF:57 +LH:25 +end_of_record diff --git a/default.isar b/default.isar new file mode 100644 index 0000000..08d9137 Binary files /dev/null and b/default.isar differ diff --git a/test/widget_test.dart b/default.isar-lck similarity index 100% rename from test/widget_test.dart rename to default.isar-lck diff --git a/isar.dll b/isar.dll new file mode 100644 index 0000000..a0c15f4 Binary files /dev/null and b/isar.dll differ diff --git a/lib/app/data/datasources/database_helper.dart b/lib/app/data/datasources/database_helper.dart new file mode 100644 index 0000000..36047f7 --- /dev/null +++ b/lib/app/data/datasources/database_helper.dart @@ -0,0 +1,88 @@ +import 'package:isar/isar.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:suji/app/domain/entities/shalat.dart'; +import 'package:suji/app/domain/entities/surah.dart'; +import 'package:suji/app/domain/entities/surah_detail.dart'; + +class DatabaseHelper { + static DatabaseHelper? _databaseHelper; + DatabaseHelper._instance() { + _databaseHelper = this; + } + + factory DatabaseHelper() => _databaseHelper ?? DatabaseHelper._instance(); + + static Isar? _database; + + Future get database async { + _database ??= await _initDb(); + return _database; + } + + Future _initDb() async { + final dir = await getApplicationDocumentsDirectory(); + final isar = await Isar.open( + [SurahSchema, SurahDetailSchema, ShalatSchema], + directory: dir.path, + ); + return isar; + } + + /// ============================================= + /// [Shalat] transactional + Future getShalatTime(int id) async { + final db = await database; + return await db?.shalats.get(id); + } + + Future getShalatTimeByDate(String date) async { + final db = await database; + return await db?.shalats.filter().dateReadableEqualTo(date).findFirst(); + } + + Future insertOrUpdateShalat(List shalat) async { + final db = await database; + await db!.writeTxn(() async { + await db.shalats.putAll(shalat); + }); + } + + /// ============================================ + /// [Surah] Transactional + Future> getAllSurah() async { + final db = await database; + return await db!.surahs.where().sortByNumber().findAll(); + } + + Future> getAyahBySurahNumber(int surahNumber) async { + final db = await database; + return await db!.surahDetails + .filter() + .numberEqualTo(surahNumber) + .sortByVersesNumberInSurah() + .findAll(); + } + + Future insertOrUpdateSurah(List listSurah) async { + final db = await database; + await db!.writeTxn(() async { + await db.surahs.putAll(listSurah); + }); + } + + Future insertOrUpdateAyah(List listAyah) async { + final db = await database; + await db!.writeTxn(() async { + await db.surahDetails.putAll(listAyah); + }); + } + + Future> getSurahByQuery(String query) async { + final db = await database; + return await db!.surahs + .filter() + .nameTransliterationIdContains(query, caseSensitive: false) + .sortByNumber() + .findAll(); + } +} diff --git a/lib/app/data/datasources/shalat_local_data_source.dart b/lib/app/data/datasources/shalat_local_data_source.dart new file mode 100644 index 0000000..e5c7101 --- /dev/null +++ b/lib/app/data/datasources/shalat_local_data_source.dart @@ -0,0 +1,43 @@ +import 'package:geolocator/geolocator.dart'; +import 'package:intl/intl.dart'; +import 'package:suji/app/data/datasources/database_helper.dart'; +import 'package:suji/app/domain/entities/shalat.dart'; +import 'package:suji/core/extensions/string_extension.dart'; +import 'package:suji/core/utils/exception.dart'; +import 'package:suji/core/utils/logger.dart'; +import 'package:suji/core/values/constant.dart'; + +abstract class ShalatLocalDataSource { + Future getShalatTime(DateTime dateTime, Position position); + Future getShalatTimeByDate(DateTime date); + Future insertOrUpdateShalat(List shalat); +} + +class ShalatLocalDataSourceImpl implements ShalatLocalDataSource { + final DatabaseHelper databaseHelper; + ShalatLocalDataSourceImpl({required this.databaseHelper}); + + @override + Future getShalatTime(DateTime dateTime, Position position) async { + final date = DateFormat('dd MMM y').format(dateTime); + Log.d('[ShalatLocalDataSourceImpl][getShalatTime]', date); + final int id = '$date${position.latitude}${position.longitude}'.fastHash(); + return await databaseHelper.getShalatTime(id); + } + + @override + Future insertOrUpdateShalat(List shalat) async { + try { + await databaseHelper.insertOrUpdateShalat(shalat); + return AppString.insertOrUpdateSuccess; + } catch (e) { + throw DatabaseException(e.toString()); + } + } + + @override + Future getShalatTimeByDate(DateTime dateTime) async { + final date = DateFormat('dd MMM y').format(dateTime); + return await databaseHelper.getShalatTimeByDate(date); + } +} diff --git a/lib/app/data/providers/shalat_remote_data_source.dart b/lib/app/data/datasources/shalat_remote_data_source.dart similarity index 61% rename from lib/app/data/providers/shalat_remote_data_source.dart rename to lib/app/data/datasources/shalat_remote_data_source.dart index 2b07c6c..500ec23 100644 --- a/lib/app/data/providers/shalat_remote_data_source.dart +++ b/lib/app/data/datasources/shalat_remote_data_source.dart @@ -6,13 +6,12 @@ import 'package:suji/core/utils/exception.dart'; import 'package:suji/core/values/constant.dart'; abstract class ShalatRemoteDataSource { - Future getShalatTime(Position position); + Future getShalatTime( + DateTime dateTime, Position position); } class ShalatRemoteDataSourceImpl extends GetConnect implements ShalatRemoteDataSource { - DateTime now = DateTime.now(); - @override void onInit() { httpClient.baseUrl = ApiConfig.shalatBaseURL; @@ -20,19 +19,15 @@ class ShalatRemoteDataSourceImpl extends GetConnect } @override - Future getShalatTime(Position position) async { - // print("Position from remote"); - // print("latitude : ${position.latitude}"); - // print("longitude : ${position.longitude}"); + Future getShalatTime( + DateTime dateTime, Position position) async { final endPoint = - '${ApiConfig.shalatCalendarEndpoint}${now.year}/${now.month}?shafaq=general&method=15&latitude=${position.latitude}&longitude=${position.longitude}'; - // print("${httpClient.baseUrl}$endPoint"); + '${ApiConfig.shalatCalendarEndpoint}${dateTime.year}/${dateTime.month}?shafaq=general&method=15&latitude=${position.latitude}&longitude=${position.longitude}'; try { final response = await get(endPoint); if (response.isOk) { return ShalatTimeResponse.fromJson(response.body); } else { - // print('masuk error handler'); throw errorHandler(response); } } catch (e) { diff --git a/lib/app/data/datasources/surah_local_data_source.dart b/lib/app/data/datasources/surah_local_data_source.dart new file mode 100644 index 0000000..03e62aa --- /dev/null +++ b/lib/app/data/datasources/surah_local_data_source.dart @@ -0,0 +1,85 @@ +import 'dart:convert'; + +import 'package:flutter/services.dart'; +import 'package:suji/app/data/model/asmaul_husna_response.dart'; +import 'package:suji/app/data/model/daily_pray_response.dart'; +import 'package:suji/app/data/datasources/database_helper.dart'; +import 'package:suji/app/domain/entities/surah.dart'; +import 'package:suji/app/domain/entities/surah_detail.dart'; +import 'package:suji/core/utils/exception.dart'; +import 'package:suji/core/values/constant.dart'; + +abstract class SurahLocalDataSource { + Future> getAllSurah(); + Future getAsmaulHusna(); + Future> getSurahByQuery(String query); + Future insertOrUpdateSurah(List listSurah); + Future insertOrUpdateAyah(List listAyah); + Future getDailyPray(); + Future> getAyahBySurahNumber(int surahNumber); +} + +class SurahLocalDataSourceImpl implements SurahLocalDataSource { + final DatabaseHelper databaseHelper; + + SurahLocalDataSourceImpl({required this.databaseHelper}); + + @override + Future> getAllSurah() async { + return await databaseHelper.getAllSurah(); + } + + @override + Future getDailyPray() async { + try { + final String response = + await rootBundle.loadString(AppAssets.jsonDailyPray); + final data = DataDailyPrayResponse.fromJson(json.decode(response)); + return data; + } catch (e) { + throw DatabaseException(AppString.databaseError); + } + } + + @override + Future getAsmaulHusna() async { + try { + final String response = + await rootBundle.loadString(AppAssets.jsonAsmaulHusna); + final data = AsmaulHusnaResponse.fromJson(json.decode(response)); + return data; + } catch (e) { + throw DatabaseException(e.toString()); + } + } + + @override + Future> getAyahBySurahNumber(int surahNumber) async { + return await databaseHelper.getAyahBySurahNumber(surahNumber); + } + + @override + Future insertOrUpdateSurah(List listSurah) async { + try { + await databaseHelper.insertOrUpdateSurah(listSurah); + return AppString.insertOrUpdateSuccess; + } catch (e) { + throw DatabaseException(e.toString()); + } + } + + @override + Future insertOrUpdateAyah(List listAyah) async { + try { + await databaseHelper.insertOrUpdateAyah(listAyah); + return AppString.insertOrUpdateSuccess; + } catch (e) { + throw DatabaseException(e.toString()); + } + } + + @override + Future> getSurahByQuery(String query) async { + return await databaseHelper.getSurahByQuery(query); + } +} diff --git a/lib/app/data/providers/surah_remote_data_source.dart b/lib/app/data/datasources/surah_remote_data_source.dart similarity index 87% rename from lib/app/data/providers/surah_remote_data_source.dart rename to lib/app/data/datasources/surah_remote_data_source.dart index f5f40f3..79fb417 100644 --- a/lib/app/data/providers/surah_remote_data_source.dart +++ b/lib/app/data/datasources/surah_remote_data_source.dart @@ -2,6 +2,7 @@ import 'package:get/get.dart'; import 'package:suji/core/utils/error_handler.dart'; import 'package:suji/core/utils/exception.dart'; import 'package:suji/app/data/model/surah_detail_response.dart'; +import 'package:suji/core/utils/logger.dart'; import 'package:suji/core/values/constant.dart'; import '../model/list_surah_response.dart'; @@ -25,9 +26,11 @@ class SurahRemoteDataSourceImpl extends GetConnect if (response.isOk) { return ListSurah.fromJson(response.body); } else { + Log.e('[SurahRemoteDataSource][getAllSurah]', '${response.statusCode}'); return errorHandler(response); } } catch (e) { + Log.e('[SurahRemoteDataSource][getAllSurah]', e.toString()); throw ServerException(e.toString()); } } diff --git a/lib/app/data/model/asmaul_husna_response.dart b/lib/app/data/model/asmaul_husna_response.dart new file mode 100644 index 0000000..f3aa915 --- /dev/null +++ b/lib/app/data/model/asmaul_husna_response.dart @@ -0,0 +1,66 @@ +// To parse this JSON data, do +// +// final asmaulHusnaResponse = asmaulHusnaResponseFromJson(jsonString); + +import 'package:suji/app/domain/entities/asmaul_husna.dart'; + +class AsmaulHusnaResponse { + final List dataAsmaulHusna; + + AsmaulHusnaResponse({ + required this.dataAsmaulHusna, + }); + + factory AsmaulHusnaResponse.fromJson(Map json) => + AsmaulHusnaResponse( + dataAsmaulHusna: List.from( + json['dataAsmaulHusna'].map((x) => DataAsmaulHusna.fromJson(x))), + ); + + Map toJson() => { + 'dataAsmaulHusna': + List.from(dataAsmaulHusna.map((x) => x.toJson())), + }; + + List toEntity() => dataAsmaulHusna + .map((e) => AsmaulHusna( + arabic: e.arabic, + index: e.index, + latin: e.latin, + translationEn: e.translationEn, + translationId: e.translationId)) + .toList(); +} + +class DataAsmaulHusna { + final String arabic; + final String index; + final String latin; + final String translationEn; + final String translationId; + + DataAsmaulHusna({ + required this.arabic, + required this.index, + required this.latin, + required this.translationEn, + required this.translationId, + }); + + factory DataAsmaulHusna.fromJson(Map json) => + DataAsmaulHusna( + arabic: json['arabic'], + index: json['index'], + latin: json['latin'], + translationEn: json['translation_en'], + translationId: json['translation_id'], + ); + + Map toJson() => { + 'arabic': arabic, + 'index': index, + 'latin': latin, + 'translation_en': translationEn, + 'translation_id': translationId, + }; +} diff --git a/lib/app/data/model/daily_pray_response.dart b/lib/app/data/model/daily_pray_response.dart new file mode 100644 index 0000000..e4de028 --- /dev/null +++ b/lib/app/data/model/daily_pray_response.dart @@ -0,0 +1,61 @@ +// To parse this JSON data, do +// +// final dataDailyPray = dataDailyPrayFromJson(jsonString); + +import 'package:suji/app/domain/entities/daily_pray.dart'; + +class DataDailyPrayResponse { + final List dataDailyPray; + + DataDailyPrayResponse({ + required this.dataDailyPray, + }); + + factory DataDailyPrayResponse.fromJson(Map json) => + DataDailyPrayResponse( + dataDailyPray: List.from( + json['dataDailyPray'].map((x) => DataDailyPrayElement.fromJson(x))), + ); + + Map toJson() => { + 'dataDailyPray': + List.from(dataDailyPray.map((x) => x.toJson())), + }; + + List toEntity() => dataDailyPray + .map((e) => DailyPray( + title: e.title, + arabic: e.arabic, + latin: e.latin, + translation: e.translation)) + .toList(); +} + +class DataDailyPrayElement { + final String title; + final String arabic; + final String latin; + final String translation; + + DataDailyPrayElement({ + required this.title, + required this.arabic, + required this.latin, + required this.translation, + }); + + factory DataDailyPrayElement.fromJson(Map json) => + DataDailyPrayElement( + title: json['title'], + arabic: json['arabic'], + latin: json['latin'], + translation: json['translation'], + ); + + Map toJson() => { + 'title': title, + 'arabic': arabic, + 'latin': latin, + 'translation': translation, + }; +} diff --git a/lib/app/data/model/shalat_time_response.dart b/lib/app/data/model/shalat_time_response.dart index 0eec67a..3b84598 100644 --- a/lib/app/data/model/shalat_time_response.dart +++ b/lib/app/data/model/shalat_time_response.dart @@ -13,17 +13,17 @@ class ShalatTimeResponse { factory ShalatTimeResponse.fromJson(Map json) => ShalatTimeResponse( - code: json["code"], - status: json["status"], - data: json["data"] == null + code: json['code'], + status: json['status'], + data: json['data'] == null ? [] - : List.from(json["data"]!.map((x) => Datum.fromJson(x))), + : List.from(json['data']!.map((x) => Datum.fromJson(x))), ); Map toJson() => { - "code": code, - "status": status, - "data": data == null + 'code': code, + 'status': status, + 'data': data == null ? [] : List.from(data!.map((x) => x.toJson())), }; @@ -69,15 +69,15 @@ class Datum { factory Datum.fromJson(Map json) => Datum( timings: - json["timings"] == null ? null : Timings.fromJson(json["timings"]), - date: json["date"] == null ? null : Date.fromJson(json["date"]), - meta: json["meta"] == null ? null : Meta.fromJson(json["meta"]), + json['timings'] == null ? null : Timings.fromJson(json['timings']), + date: json['date'] == null ? null : Date.fromJson(json['date']), + meta: json['meta'] == null ? null : Meta.fromJson(json['meta']), ); Map toJson() => { - "timings": timings?.toJson(), - "date": date?.toJson(), - "meta": meta?.toJson(), + 'timings': timings?.toJson(), + 'date': date?.toJson(), + 'meta': meta?.toJson(), }; } @@ -95,19 +95,19 @@ class Date { }); factory Date.fromJson(Map json) => Date( - readable: json["readable"], - timestamp: json["timestamp"], - gregorian: json["gregorian"] == null + readable: json['readable'], + timestamp: json['timestamp'], + gregorian: json['gregorian'] == null ? null - : Gregorian.fromJson(json["gregorian"]), - hijri: json["hijri"] == null ? null : Hijri.fromJson(json["hijri"]), + : Gregorian.fromJson(json['gregorian']), + hijri: json['hijri'] == null ? null : Hijri.fromJson(json['hijri']), ); Map toJson() => { - "readable": readable, - "timestamp": timestamp, - "gregorian": gregorian?.toJson(), - "hijri": hijri?.toJson(), + 'readable': readable, + 'timestamp': timestamp, + 'gregorian': gregorian?.toJson(), + 'hijri': hijri?.toJson(), }; } @@ -131,29 +131,29 @@ class Gregorian { }); factory Gregorian.fromJson(Map json) => Gregorian( - date: json["date"], - format: json["format"], - day: json["day"], - weekday: json["weekday"] == null + date: json['date'], + format: json['format'], + day: json['day'], + weekday: json['weekday'] == null ? null - : GregorianWeekday.fromJson(json["weekday"]), - month: json["month"] == null + : GregorianWeekday.fromJson(json['weekday']), + month: json['month'] == null ? null - : GregorianMonth.fromJson(json["month"]), - year: json["year"], - designation: json["designation"] == null + : GregorianMonth.fromJson(json['month']), + year: json['year'], + designation: json['designation'] == null ? null - : Designation.fromJson(json["designation"]), + : Designation.fromJson(json['designation']), ); Map toJson() => { - "date": date, - "format": format, - "day": day, - "weekday": weekday?.toJson(), - "month": month?.toJson(), - "year": year, - "designation": designation?.toJson(), + 'date': date, + 'format': format, + 'day': day, + 'weekday': weekday?.toJson(), + 'month': month?.toJson(), + 'year': year, + 'designation': designation?.toJson(), }; } @@ -167,13 +167,13 @@ class Designation { }); factory Designation.fromJson(Map json) => Designation( - abbreviated: json["abbreviated"], - expanded: json["expanded"], + abbreviated: json['abbreviated'], + expanded: json['expanded'], ); Map toJson() => { - "abbreviated": abbreviated, - "expanded": expanded, + 'abbreviated': abbreviated, + 'expanded': expanded, }; } @@ -187,13 +187,13 @@ class GregorianMonth { }); factory GregorianMonth.fromJson(Map json) => GregorianMonth( - number: json["number"], - en: json["en"], + number: json['number'], + en: json['en'], ); Map toJson() => { - "number": number, - "en": en, + 'number': number, + 'en': en, }; } @@ -206,11 +206,11 @@ class GregorianWeekday { factory GregorianWeekday.fromJson(Map json) => GregorianWeekday( - en: json["en"], + en: json['en'], ); Map toJson() => { - "en": en, + 'en': en, }; } @@ -236,32 +236,32 @@ class Hijri { }); factory Hijri.fromJson(Map json) => Hijri( - date: json["date"], - format: json["format"], - day: json["day"], - weekday: json["weekday"] == null + date: json['date'], + format: json['format'], + day: json['day'], + weekday: json['weekday'] == null ? null - : HijriWeekday.fromJson(json["weekday"]), + : HijriWeekday.fromJson(json['weekday']), month: - json["month"] == null ? null : HijriMonth.fromJson(json["month"]), - year: json["year"], - designation: json["designation"] == null + json['month'] == null ? null : HijriMonth.fromJson(json['month']), + year: json['year'], + designation: json['designation'] == null ? null - : Designation.fromJson(json["designation"]), - holidays: json["holidays"] == null + : Designation.fromJson(json['designation']), + holidays: json['holidays'] == null ? [] - : List.from(json["holidays"]!.map((x) => x)), + : List.from(json['holidays']!.map((x) => x)), ); Map toJson() => { - "date": date, - "format": format, - "day": day, - "weekday": weekday?.toJson(), - "month": month?.toJson(), - "year": year, - "designation": designation?.toJson(), - "holidays": + 'date': date, + 'format': format, + 'day': day, + 'weekday': weekday?.toJson(), + 'month': month?.toJson(), + 'year': year, + 'designation': designation?.toJson(), + 'holidays': holidays == null ? [] : List.from(holidays!.map((x) => x)), }; } @@ -278,15 +278,15 @@ class HijriMonth { }); factory HijriMonth.fromJson(Map json) => HijriMonth( - number: json["number"], - en: json["en"], - ar: json["ar"], + number: json['number'], + en: json['en'], + ar: json['ar'], ); Map toJson() => { - "number": number, - "en": en, - "ar": ar, + 'number': number, + 'en': en, + 'ar': ar, }; } @@ -300,13 +300,13 @@ class HijriWeekday { }); factory HijriWeekday.fromJson(Map json) => HijriWeekday( - en: json["en"], - ar: json["ar"], + en: json['en'], + ar: json['ar'], ); Map toJson() => { - "en": en, - "ar": ar, + 'en': en, + 'ar': ar, }; } @@ -332,26 +332,26 @@ class Meta { }); factory Meta.fromJson(Map json) => Meta( - latitude: json["latitude"]?.toDouble(), - longitude: json["longitude"]?.toDouble(), - timezone: json["timezone"], - method: json["method"] == null ? null : Method.fromJson(json["method"]), - latitudeAdjustmentMethod: json["latitudeAdjustmentMethod"], - midnightMode: json["midnightMode"], - school: json["school"], - offset: Map.from(json["offset"]!) + latitude: json['latitude']?.toDouble(), + longitude: json['longitude']?.toDouble(), + timezone: json['timezone'], + method: json['method'] == null ? null : Method.fromJson(json['method']), + latitudeAdjustmentMethod: json['latitudeAdjustmentMethod'], + midnightMode: json['midnightMode'], + school: json['school'], + offset: Map.from(json['offset']!) .map((k, v) => MapEntry(k, v)), ); Map toJson() => { - "latitude": latitude, - "longitude": longitude, - "timezone": timezone, - "method": method?.toJson(), - "latitudeAdjustmentMethod": latitudeAdjustmentMethod, - "midnightMode": midnightMode, - "school": school, - "offset": + 'latitude': latitude, + 'longitude': longitude, + 'timezone': timezone, + 'method': method?.toJson(), + 'latitudeAdjustmentMethod': latitudeAdjustmentMethod, + 'midnightMode': midnightMode, + 'school': school, + 'offset': Map.from(offset!).map((k, v) => MapEntry(k, v)), }; } @@ -368,15 +368,15 @@ class Method { }); factory Method.fromJson(Map json) => Method( - id: json["id"], - name: json["name"], - params: json["params"] == null ? null : Params.fromJson(json["params"]), + id: json['id'], + name: json['name'], + params: json['params'] == null ? null : Params.fromJson(json['params']), ); Map toJson() => { - "id": id, - "name": name, - "params": params?.toJson(), + 'id': id, + 'name': name, + 'params': params?.toJson(), }; } @@ -388,11 +388,11 @@ class Params { }); factory Params.fromJson(Map json) => Params( - shafaq: json["shafaq"], + shafaq: json['shafaq'], ); Map toJson() => { - "shafaq": shafaq, + 'shafaq': shafaq, }; } @@ -424,30 +424,30 @@ class Timings { }); factory Timings.fromJson(Map json) => Timings( - fajr: json["Fajr"], - sunrise: json["Sunrise"], - dhuhr: json["Dhuhr"], - asr: json["Asr"], - sunset: json["Sunset"], - maghrib: json["Maghrib"], - isha: json["Isha"], - imsak: json["Imsak"], - midnight: json["Midnight"], - firstthird: json["Firstthird"], - lastthird: json["Lastthird"], + fajr: json['Fajr'], + sunrise: json['Sunrise'], + dhuhr: json['Dhuhr'], + asr: json['Asr'], + sunset: json['Sunset'], + maghrib: json['Maghrib'], + isha: json['Isha'], + imsak: json['Imsak'], + midnight: json['Midnight'], + firstthird: json['Firstthird'], + lastthird: json['Lastthird'], ); Map toJson() => { - "Fajr": fajr, - "Sunrise": sunrise, - "Dhuhr": dhuhr, - "Asr": asr, - "Sunset": sunset, - "Maghrib": maghrib, - "Isha": isha, - "Imsak": imsak, - "Midnight": midnight, - "Firstthird": firstthird, - "Lastthird": lastthird, + 'Fajr': fajr, + 'Sunrise': sunrise, + 'Dhuhr': dhuhr, + 'Asr': asr, + 'Sunset': sunset, + 'Maghrib': maghrib, + 'Isha': isha, + 'Imsak': imsak, + 'Midnight': midnight, + 'Firstthird': firstthird, + 'Lastthird': lastthird, }; } diff --git a/lib/app/data/model/surah_detail_response.dart b/lib/app/data/model/surah_detail_response.dart index 6b41cb6..040cb64 100644 --- a/lib/app/data/model/surah_detail_response.dart +++ b/lib/app/data/model/surah_detail_response.dart @@ -26,17 +26,17 @@ class SurahDetailResponse { factory SurahDetailResponse.fromJson(Map json) => SurahDetailResponse( - code: json["code"], - status: json["status"], - message: json["message"], - data: json["data"] == null ? null : Data.fromJson(json["data"]), + code: json['code'], + status: json['status'], + message: json['message'], + data: json['data'] == null ? null : Data.fromJson(json['data']), ); Map toJson() => { - "code": code, - "status": status, - "message": message, - "data": data?.toJson(), + 'code': code, + 'status': status, + 'message': message, + 'data': data?.toJson(), }; List toEntity() { final surahDetail = data?.verses; @@ -44,23 +44,24 @@ class SurahDetailResponse { ?.map((e) => SurahDetail( number: data?.number ?? 0, numberOfVerses: data?.numberOfVerses ?? 0, - nameShort: data?.name?.short ?? "null", - nameLong: data?.name?.long ?? "", - nameTransliterationId: data?.name?.transliteration?.id ?? "", - nameTranslationId: data?.name?.translation?.id ?? "", - relevationId: data?.revelation?.id ?? "", + nameShort: data?.name?.short ?? 'null', + nameLong: data?.name?.long ?? '', + nameTransliterationId: data?.name?.transliteration?.id ?? '', + nameTranslationId: data?.name?.translation?.id ?? '', + relevationId: data?.revelation?.id ?? '', + tafsirId: data?.tafsir?.id ?? '', preBismillahTextArab: data?.preBismillah?.text?.arab ?? e.text?.arab ?? - "بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ", + 'بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ', preBismillahAudioPrimary: data?.preBismillah?.audio?.primary ?? e.audio?.primary ?? - "https://cdn.alquran.cloud/media/audio/ayah/ar.alafasy/1", + 'https://cdn.alquran.cloud/media/audio/ayah/ar.alafasy/1', versesNumberInQuran: e.number?.inQuran ?? 0, versesNumberInSurah: e.number?.inSurah ?? 0, - versesTextArab: e.text?.arab ?? "", - versesTranslationId: e.translation?.id ?? "", - versesAudioPrimary: e.audio?.primary ?? "", - versesTafsirIdShort: e.tafsir?.id?.short ?? "")) + versesTextArab: e.text?.arab ?? '', + versesTranslationId: e.translation?.id ?? '', + versesAudioPrimary: e.audio?.primary ?? '', + versesTafsirIdShort: e.tafsir?.id?.short ?? '')) .toList() ?? []; } @@ -88,32 +89,32 @@ class Data { }); factory Data.fromJson(Map json) => Data( - number: json["number"], - sequence: json["sequence"], - numberOfVerses: json["numberOfVerses"], - name: json["name"] == null ? null : Name.fromJson(json["name"]), - revelation: json["revelation"] == null + number: json['number'], + sequence: json['sequence'], + numberOfVerses: json['numberOfVerses'], + name: json['name'] == null ? null : Name.fromJson(json['name']), + revelation: json['revelation'] == null ? null - : Revelation.fromJson(json["revelation"]), + : Revelation.fromJson(json['revelation']), tafsir: - json["tafsir"] == null ? null : DataTafsir.fromJson(json["tafsir"]), - preBismillah: json["preBismillah"] == null + json['tafsir'] == null ? null : DataTafsir.fromJson(json['tafsir']), + preBismillah: json['preBismillah'] == null ? null - : PreBismillah.fromJson(json["preBismillah"]), - verses: json["verses"] == null + : PreBismillah.fromJson(json['preBismillah']), + verses: json['verses'] == null ? [] - : List.from(json["verses"]!.map((x) => Verse.fromJson(x))), + : List.from(json['verses']!.map((x) => Verse.fromJson(x))), ); Map toJson() => { - "number": number, - "sequence": sequence, - "numberOfVerses": numberOfVerses, - "name": name?.toJson(), - "revelation": revelation?.toJson(), - "tafsir": tafsir?.toJson(), - "preBismillah": preBismillah?.toJson(), - "verses": verses == null + 'number': number, + 'sequence': sequence, + 'numberOfVerses': numberOfVerses, + 'name': name?.toJson(), + 'revelation': revelation?.toJson(), + 'tafsir': tafsir?.toJson(), + 'preBismillah': preBismillah?.toJson(), + 'verses': verses == null ? [] : List.from(verses!.map((x) => x.toJson())), }; @@ -133,21 +134,21 @@ class Name { }); factory Name.fromJson(Map json) => Name( - short: json["short"], - long: json["long"], - transliteration: json["transliteration"] == null + short: json['short'], + long: json['long'], + transliteration: json['transliteration'] == null ? null - : Translation.fromJson(json["transliteration"]), - translation: json["translation"] == null + : Translation.fromJson(json['transliteration']), + translation: json['translation'] == null ? null - : Translation.fromJson(json["translation"]), + : Translation.fromJson(json['translation']), ); Map toJson() => { - "short": short, - "long": long, - "transliteration": transliteration?.toJson(), - "translation": translation?.toJson(), + 'short': short, + 'long': long, + 'transliteration': transliteration?.toJson(), + 'translation': translation?.toJson(), }; } @@ -161,13 +162,13 @@ class Translation { }); factory Translation.fromJson(Map json) => Translation( - en: json["en"], - id: json["id"], + en: json['en'], + id: json['id'], ); Map toJson() => { - "en": en, - "id": id, + 'en': en, + 'id': id, }; } @@ -183,17 +184,17 @@ class PreBismillah { }); factory PreBismillah.fromJson(Map json) => PreBismillah( - text: json["text"] == null ? null : Text.fromJson(json["text"]), - translation: json["translation"] == null + text: json['text'] == null ? null : Text.fromJson(json['text']), + translation: json['translation'] == null ? null - : Translation.fromJson(json["translation"]), - audio: json["audio"] == null ? null : Audio.fromJson(json["audio"]), + : Translation.fromJson(json['translation']), + audio: json['audio'] == null ? null : Audio.fromJson(json['audio']), ); Map toJson() => { - "text": text?.toJson(), - "translation": translation?.toJson(), - "audio": audio?.toJson(), + 'text': text?.toJson(), + 'translation': translation?.toJson(), + 'audio': audio?.toJson(), }; } @@ -207,15 +208,15 @@ class Audio { }); factory Audio.fromJson(Map json) => Audio( - primary: json["primary"], - secondary: json["secondary"] == null + primary: json['primary'], + secondary: json['secondary'] == null ? [] - : List.from(json["secondary"]!.map((x) => x)), + : List.from(json['secondary']!.map((x) => x)), ); Map toJson() => { - "primary": primary, - "secondary": secondary == null + 'primary': primary, + 'secondary': secondary == null ? [] : List.from(secondary!.map((x) => x)), }; @@ -231,15 +232,15 @@ class Text { }); factory Text.fromJson(Map json) => Text( - arab: json["arab"], - transliteration: json["transliteration"] == null + arab: json['arab'], + transliteration: json['transliteration'] == null ? null - : Transliteration.fromJson(json["transliteration"]), + : Transliteration.fromJson(json['transliteration']), ); Map toJson() => { - "arab": arab, - "transliteration": transliteration?.toJson(), + 'arab': arab, + 'transliteration': transliteration?.toJson(), }; } @@ -252,11 +253,11 @@ class Transliteration { factory Transliteration.fromJson(Map json) => Transliteration( - en: json["en"], + en: json['en'], ); Map toJson() => { - "en": en, + 'en': en, }; } @@ -272,15 +273,15 @@ class Revelation { }); factory Revelation.fromJson(Map json) => Revelation( - arab: json["arab"], - en: json["en"], - id: json["id"], + arab: json['arab'], + en: json['en'], + id: json['id'], ); Map toJson() => { - "arab": arab, - "en": en, - "id": id, + 'arab': arab, + 'en': en, + 'id': id, }; } @@ -292,11 +293,11 @@ class DataTafsir { }); factory DataTafsir.fromJson(Map json) => DataTafsir( - id: json["id"], + id: json['id'], ); Map toJson() => { - "id": id, + 'id': id, }; } @@ -318,25 +319,25 @@ class Verse { }); factory Verse.fromJson(Map json) => Verse( - number: json["number"] == null ? null : Number.fromJson(json["number"]), - meta: json["meta"] == null ? null : Meta.fromJson(json["meta"]), - text: json["text"] == null ? null : Text.fromJson(json["text"]), - translation: json["translation"] == null + number: json['number'] == null ? null : Number.fromJson(json['number']), + meta: json['meta'] == null ? null : Meta.fromJson(json['meta']), + text: json['text'] == null ? null : Text.fromJson(json['text']), + translation: json['translation'] == null ? null - : Translation.fromJson(json["translation"]), - audio: json["audio"] == null ? null : Audio.fromJson(json["audio"]), - tafsir: json["tafsir"] == null + : Translation.fromJson(json['translation']), + audio: json['audio'] == null ? null : Audio.fromJson(json['audio']), + tafsir: json['tafsir'] == null ? null - : VerseTafsir.fromJson(json["tafsir"]), + : VerseTafsir.fromJson(json['tafsir']), ); Map toJson() => { - "number": number?.toJson(), - "meta": meta?.toJson(), - "text": text?.toJson(), - "translation": translation?.toJson(), - "audio": audio?.toJson(), - "tafsir": tafsir?.toJson(), + 'number': number?.toJson(), + 'meta': meta?.toJson(), + 'text': text?.toJson(), + 'translation': translation?.toJson(), + 'audio': audio?.toJson(), + 'tafsir': tafsir?.toJson(), }; } @@ -358,21 +359,21 @@ class Meta { }); factory Meta.fromJson(Map json) => Meta( - juz: json["juz"], - page: json["page"], - manzil: json["manzil"], - ruku: json["ruku"], - hizbQuarter: json["hizbQuarter"], - sajda: json["sajda"] == null ? null : Sajda.fromJson(json["sajda"]), + juz: json['juz'], + page: json['page'], + manzil: json['manzil'], + ruku: json['ruku'], + hizbQuarter: json['hizbQuarter'], + sajda: json['sajda'] == null ? null : Sajda.fromJson(json['sajda']), ); Map toJson() => { - "juz": juz, - "page": page, - "manzil": manzil, - "ruku": ruku, - "hizbQuarter": hizbQuarter, - "sajda": sajda?.toJson(), + 'juz': juz, + 'page': page, + 'manzil': manzil, + 'ruku': ruku, + 'hizbQuarter': hizbQuarter, + 'sajda': sajda?.toJson(), }; } @@ -386,13 +387,13 @@ class Sajda { }); factory Sajda.fromJson(Map json) => Sajda( - recommended: json["recommended"], - obligatory: json["obligatory"], + recommended: json['recommended'], + obligatory: json['obligatory'], ); Map toJson() => { - "recommended": recommended, - "obligatory": obligatory, + 'recommended': recommended, + 'obligatory': obligatory, }; } @@ -406,13 +407,13 @@ class Number { }); factory Number.fromJson(Map json) => Number( - inQuran: json["inQuran"], - inSurah: json["inSurah"], + inQuran: json['inQuran'], + inSurah: json['inSurah'], ); Map toJson() => { - "inQuran": inQuran, - "inSurah": inSurah, + 'inQuran': inQuran, + 'inSurah': inSurah, }; } @@ -424,11 +425,11 @@ class VerseTafsir { }); factory VerseTafsir.fromJson(Map json) => VerseTafsir( - id: json["id"] == null ? null : Id.fromJson(json["id"]), + id: json['id'] == null ? null : Id.fromJson(json['id']), ); Map toJson() => { - "id": id?.toJson(), + 'id': id?.toJson(), }; } @@ -442,13 +443,13 @@ class Id { }); factory Id.fromJson(Map json) => Id( - short: json["short"], - long: json["long"], + short: json['short'], + long: json['long'], ); Map toJson() => { - "short": short, - "long": long, + 'short': short, + 'long': long, }; } diff --git a/lib/app/data/model/surah_response.dart b/lib/app/data/model/surah_response.dart deleted file mode 100644 index 14a8a6a..0000000 --- a/lib/app/data/model/surah_response.dart +++ /dev/null @@ -1,39 +0,0 @@ -// import 'package:freezed_annotation/freezed_annotation.dart'; - -// part 'surah_response.freezed.dart'; - -// @freezed -// class SurahResponse with _$SurahResponse { -// const factory SurahResponse(int? code, String? status, String? message, -// List? surahList) = _SurahResponse; -// } - -// @freezed -// class SurahModelResponse with _$SurahModelResponse { -// const factory SurahModelResponse( -// int? number, -// int? sequence, -// int? numberOfVerses, -// SurahNameResponse? name, -// SurahRevelationResponse? revelation, -// SurahTafsirResponse? tafsir, -// ) = _SurahModelResponse; -// } - -// @freezed -// class SurahNameResponse with _$SurahNameResponse { -// const factory SurahNameResponse( -// String? short, -// String? long, -// SurahTransliterationResponse? transliteration, -// SurahTransliterationResponse? translation, -// ) = _SurahNameResponse; -// } - -// @freezed -// class SurahTransliterationResponse with _$SurahTransliterationResponse { -// const factory SurahTransliterationResponse( -// String? en, -// String? id, -// ) = _SurahTransliterationResponse; -// } diff --git a/lib/app/data/providers/database_helper.dart b/lib/app/data/providers/database_helper.dart deleted file mode 100644 index 520e1dd..0000000 --- a/lib/app/data/providers/database_helper.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:isar/isar.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:suji/app/domain/entities/shalat.dart'; -import 'package:suji/app/domain/entities/surah.dart'; -import 'package:suji/app/domain/entities/surah_detail.dart'; - -class DatabaseHelper { - static DatabaseHelper? _databaseHelper; - DatabaseHelper._instance() { - _databaseHelper = this; - } - - factory DatabaseHelper() => _databaseHelper ?? DatabaseHelper._instance(); - - static Isar? _database; - - Future get database async { - _database ??= await _initDb(); - return _database; - } - - Future _initDb() async { - final dir = await getApplicationDocumentsDirectory(); - final isar = await Isar.open( - [SurahSchema, SurahDetailSchema, ShalatSchema], - directory: dir.path, - ); - return isar; - } -} diff --git a/lib/app/data/providers/shalat_local_data_source.dart b/lib/app/data/providers/shalat_local_data_source.dart deleted file mode 100644 index 7856e08..0000000 --- a/lib/app/data/providers/shalat_local_data_source.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:geolocator/geolocator.dart'; -import 'package:get/get.dart'; -import 'package:intl/intl.dart'; -import 'package:isar/isar.dart'; -import 'package:suji/app/data/providers/database_helper.dart'; -import 'package:suji/app/domain/entities/shalat.dart'; -import 'package:suji/core/extensions/string_extension.dart'; -import 'package:suji/core/utils/exception.dart'; -import 'package:suji/core/utils/logger.dart'; - -abstract class ShalatLocalDataSource { - Future getShalatTime(Position position); - Future getShalatTimeByDate(DateTime date); - Future insertOrUpdateShalat(List shalat); -} - -class ShalatLocalDataSourceImpl implements ShalatLocalDataSource { - final DatabaseHelper databaseHelper = Get.find(); - - @override - Future getShalatTime(Position position) async { - final db = await databaseHelper.database; - final now = DateFormat('dd MMM y').format(DateTime.now()); - Log.d('[ShalatLocalDataSourceImpl][getShalatTime]', now); - return db!.shalats - .get('$now${position.latitude}${position.longitude}'.fastHash()); - } - - @override - Future insertOrUpdateShalat(List shalat) async { - try { - final db = await databaseHelper.database; - await db!.writeTxn(() async { - await db.shalats.putAll(shalat); - }); - } catch (e) { - throw DatabaseException(e.toString()); - } - } - - @override - Future getShalatTimeByDate(DateTime date) async { - final db = await databaseHelper.database; - final todayFormated = DateFormat('dd MMM y').format(date); - return db!.shalats.filter().dateReadableEqualTo(todayFormated).findFirst(); - } -} diff --git a/lib/app/data/providers/surah_local_data_source.dart b/lib/app/data/providers/surah_local_data_source.dart deleted file mode 100644 index d1ea875..0000000 --- a/lib/app/data/providers/surah_local_data_source.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:get/get.dart'; -import 'package:isar/isar.dart'; -import 'package:suji/app/data/providers/database_helper.dart'; -import 'package:suji/app/domain/entities/surah.dart'; -import 'package:suji/app/domain/entities/surah_detail.dart'; -import 'package:suji/core/utils/exception.dart'; - -abstract class SurahLocalDataSource { - Future> getAllSurah(); - Future> getSurahByQuery(String query); - Future insertOrUpdateSurah(List listSurah); - Future insertOrUpdateAyah(List listAyah); - Future> getAyahBySurahNumber(int surahNumber); -} - -class SurahLocalDataSourceImpl implements SurahLocalDataSource { - final DatabaseHelper databaseHelper = Get.find(); - - @override - Future> getAllSurah() async { - final db = await databaseHelper.database; - return db!.surahs.where().sortByNumber().findAll(); - } - - @override - Future> getAyahBySurahNumber(int surahNumber) async { - final db = await databaseHelper.database; - return db!.surahDetails - .filter() - .numberEqualTo(surahNumber) - .sortByVersesNumberInSurah() - .findAll(); - } - - @override - Future insertOrUpdateSurah(List listSurah) async { - try { - final db = await databaseHelper.database; - await db!.writeTxn(() async { - await db.surahs.putAll(listSurah); - }); - } catch (e) { - throw DatabaseException(e.toString()); - } - } - - @override - Future insertOrUpdateAyah(List listAyah) async { - try { - final db = await databaseHelper.database; - await db!.writeTxn(() async { - await db.surahDetails.putAll(listAyah); - }); - } catch (e) { - throw DatabaseException(e.toString()); - } - } - - @override - Future> getSurahByQuery(String query) async { - final db = await databaseHelper.database; - return db!.surahs - .filter() - .nameTransliterationIdContains(query, caseSensitive: false) - .sortByNumber() - .findAll(); - } -} diff --git a/lib/app/data/repository/shalat_repository_impl.dart b/lib/app/data/repository/shalat_repository_impl.dart index 52b45c7..38e09a0 100644 --- a/lib/app/data/repository/shalat_repository_impl.dart +++ b/lib/app/data/repository/shalat_repository_impl.dart @@ -1,29 +1,36 @@ +import 'dart:io'; + import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dartz/dartz.dart'; import 'package:geolocator/geolocator.dart'; -import 'package:get/get.dart'; -import 'package:suji/app/data/providers/shalat_local_data_source.dart'; -import 'package:suji/app/data/providers/shalat_remote_data_source.dart'; +import 'package:suji/app/data/datasources/shalat_local_data_source.dart'; +import 'package:suji/app/data/datasources/shalat_remote_data_source.dart'; import 'package:suji/app/domain/entities/shalat.dart'; import 'package:suji/app/domain/repository/shalat_repository.dart'; import 'package:suji/core/utils/exception.dart'; import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; class ShalatRepositoryImpl implements ShalatRepository { - final ShalatRemoteDataSource shalatRemoteDataSource = - Get.put(ShalatRemoteDataSourceImpl()); - final ShalatLocalDataSource shalatLocalDataSource = - Get.put(ShalatLocalDataSourceImpl()); + final ShalatRemoteDataSource shalatRemoteDataSource; + final ShalatLocalDataSource shalatLocalDataSource; + final Connectivity connectivity; + + ShalatRepositoryImpl( + {required this.shalatRemoteDataSource, + required this.shalatLocalDataSource, + required this.connectivity}); @override - Future> getShalatTime(Position position) async { + Future> getShalatTime( + DateTime dateTime, Position position) async { try { - final result = await shalatLocalDataSource.getShalatTime(position); - // print('repository: $result'); + final result = + await shalatLocalDataSource.getShalatTime(dateTime, position); if (result == null) { - await fetchShalatTime(position); - final data = await shalatLocalDataSource.getShalatTime(position); - return Right(data!); + final data = await fetchShalatTime(dateTime, position); + await shalatLocalDataSource.insertOrUpdateShalat(data); + return getShalatTime(dateTime, position); } else { return Right(result); } @@ -31,20 +38,23 @@ class ShalatRepositoryImpl implements ShalatRepository { return Left(ServerFailure(e.message)); } on DatabaseException catch (e) { return Left(DatabaseFailure(e.message)); + } on SocketException catch (e) { + return Left(ConnectionFailure(e.message)); } catch (e) { return Left(ServerFailure(e.toString())); } } - @override - Future fetchShalatTime(Position position) async { - final hasConnected = await Connectivity().checkConnectivity(); + Future> fetchShalatTime( + DateTime dateTime, Position position) async { + final hasConnected = await connectivity.checkConnectivity(); if (hasConnected == ConnectivityResult.mobile || hasConnected == ConnectivityResult.wifi) { - final result = await shalatRemoteDataSource.getShalatTime(position); - await shalatLocalDataSource.insertOrUpdateShalat(result.toEntity()); + final result = + await shalatRemoteDataSource.getShalatTime(dateTime, position); + return result.toEntity(); } else { - throw ServerException('Check your connection'); + throw const SocketException(AppString.socketException); } } } diff --git a/lib/app/data/repository/surah_repository_impl.dart b/lib/app/data/repository/surah_repository_impl.dart index 435f63a..c3a5996 100644 --- a/lib/app/data/repository/surah_repository_impl.dart +++ b/lib/app/data/repository/surah_repository_impl.dart @@ -1,51 +1,72 @@ +import 'dart:io'; + import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dartz/dartz.dart'; -import 'package:get/get.dart'; -import 'package:suji/app/data/providers/surah_local_data_source.dart'; +import 'package:suji/app/data/datasources/surah_local_data_source.dart'; +import 'package:suji/app/domain/entities/asmaul_husna.dart'; +import 'package:suji/app/domain/entities/daily_pray.dart'; import 'package:suji/app/domain/repository/surah_repository.dart'; import 'package:suji/core/utils/exception.dart'; import 'package:suji/core/utils/failure.dart'; -import 'package:suji/app/data/providers/surah_remote_data_source.dart'; +import 'package:suji/app/data/datasources/surah_remote_data_source.dart'; import 'package:suji/app/domain/entities/surah.dart'; import 'package:suji/app/domain/entities/surah_detail.dart'; +import 'package:suji/core/values/constant.dart'; class SurahRepositoryImpl implements SurahRepository { - final SurahRemoteDataSource api = Get.find(); - final SurahLocalDataSource local = Get.find(); + final SurahRemoteDataSource surahRemoteDataSource; + final SurahLocalDataSource surahLocalDataSource; + final Connectivity connectivity; + + SurahRepositoryImpl( + {required this.surahRemoteDataSource, + required this.surahLocalDataSource, + required this.connectivity}); + + @override + Future>> getDailyPray() async { + try { + final result = await surahLocalDataSource.getDailyPray(); + return Right(result.toEntity()); + } on DatabaseException catch (e) { + return Left(DatabaseFailure(e.message)); + } catch (e) { + return Left(DatabaseFailure(e.toString())); + } + } - // @override - Future fetchAllSurah() async { - // try { - final hasConnected = await Connectivity().checkConnectivity(); + @override + Future>> getAsmaulHusna() async { + try { + final result = await surahLocalDataSource.getAsmaulHusna(); + return Right(result.toEntity()); + } on DatabaseException catch (e) { + return Left(DatabaseFailure(e.message)); + } catch (e) { + return Left(DatabaseFailure(e.toString())); + } + } + + Future> fetchAllSurah() async { + final hasConnected = await connectivity.checkConnectivity(); if (hasConnected == ConnectivityResult.mobile || hasConnected == ConnectivityResult.wifi) { - final result = await api.getAllSurah(); - await local.insertOrUpdateSurah(result.toEntity()); + final result = await surahRemoteDataSource.getAllSurah(); + return result.toEntity(); } else { - throw ServerException('Check your connection'); + throw const SocketException(AppString.socketException); } - // return getAllSurahCache(); - // } on ServerException catch (e) { - // Get.snackbar( - // "Information", - // e.toString(), - // backgroundColor: Colors.red, - // ); - // return Left(ServerFailure(e.toString())); - // return getAllSurahCache(); - // } on DatabaseException catch (e) { - // return Left(DatabaseFailure(e.toString())); - // } } - Future fetchAyahBySurahNumber(int surahNumber) async { - final hasConnected = await Connectivity().checkConnectivity(); + Future> fetchAyahBySurahNumber(int surahNumber) async { + final hasConnected = await connectivity.checkConnectivity(); if (hasConnected == ConnectivityResult.mobile || hasConnected == ConnectivityResult.wifi) { - final result = await api.getAyahBySurahNumber(surahNumber); - await local.insertOrUpdateAyah(result.toEntity()); + final result = + await surahRemoteDataSource.getAyahBySurahNumber(surahNumber); + return result.toEntity(); } else { - throw ServerException('Check your connection'); + throw const SocketException(AppString.socketException); } } @@ -53,84 +74,52 @@ class SurahRepositoryImpl implements SurahRepository { Future>> getAyahBySurahNumber( int surahNumber) async { try { - // final hasConnected = await Connectivity().checkConnectivity(); - // if (hasConnected == ConnectivityResult.mobile || - // hasConnected == ConnectivityResult.wifi) { - // final result = await api.getAyahBySurahNumber(surahNumber); - // await local.insertOrUpdateAyah(result.toEntity()); - // } - // return getAyahBySurahNumberCache(surahNumber); - - final data = await local.getAyahBySurahNumber(surahNumber); + final data = await surahLocalDataSource.getAyahBySurahNumber(surahNumber); if (data.isEmpty) { - await fetchAyahBySurahNumber(surahNumber); - final newData = await local.getAyahBySurahNumber(surahNumber); - return Right(newData); + final result = await fetchAyahBySurahNumber(surahNumber); + await surahLocalDataSource.insertOrUpdateAyah(result); + return Right(result); } return Right(data); } on ServerException catch (e) { - // return getAyahBySurahNumberCache(surahNumber); return Left(ServerFailure(e.message)); } on DatabaseException catch (e) { return Left(DatabaseFailure(e.message)); - } - } - - @override - Future> insertOrUpdateSurah( - List listSurah) async { - try { - await local.insertOrUpdateSurah(listSurah); - return const Right("Insert Success"); + } on SocketException catch (e) { + return Left(ConnectionFailure(e.message)); } catch (e) { - return Left(DatabaseFailure(e.toString())); + return Left(ServerFailure(e.toString())); } } @override Future>> getAllSurah() async { try { - final data = await local.getAllSurah(); + final data = await surahLocalDataSource.getAllSurah(); if (data.isEmpty) { - await fetchAllSurah(); - final newData = await local.getAllSurah(); - // print(newData); - return Right(newData); + final result = await fetchAllSurah(); + await surahLocalDataSource.insertOrUpdateSurah(result); + return Right(result); } return Right(data); } on DatabaseException catch (e) { return Left(DatabaseFailure(e.message)); } on ServerException catch (e) { return Left(ServerFailure(e.message)); - } - } - - Future>> getAyahBySurahNumberCache( - int surahNumber) async { - try { - final data = await local.getAyahBySurahNumber(surahNumber); - return Right(data); - } on DatabaseException catch (e) { - return Left(DatabaseFailure(e.toString())); + } on SocketException catch (e) { + return Left(ConnectionFailure(e.message)); + } catch (e) { + return Left(ServerFailure(e.toString())); } } @override Future>> getSurahByQuery(query) async { try { - final data = await local.getSurahByQuery(query); + final data = await surahLocalDataSource.getSurahByQuery(query); return Right(data); } on DatabaseException catch (e) { - return Left(DatabaseFailure(e.toString())); - } - } - - @override - Future> insertOrUpdateAyah( - List listAyah) async { - try { - await local.insertOrUpdateAyah(listAyah); - return const Right("Insert Success"); + return Left(DatabaseFailure(e.message)); } catch (e) { return Left(DatabaseFailure(e.toString())); } diff --git a/lib/app/data/services/background_service.dart b/lib/app/data/services/background_service.dart index d1960b2..3ada108 100644 --- a/lib/app/data/services/background_service.dart +++ b/lib/app/data/services/background_service.dart @@ -2,7 +2,9 @@ import 'dart:isolate'; import 'dart:ui'; import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart'; -import 'package:suji/app/data/providers/shalat_local_data_source.dart'; +import 'package:get/get.dart'; +import 'package:suji/app/data/datasources/database_helper.dart'; +import 'package:suji/app/data/datasources/shalat_local_data_source.dart'; import 'package:suji/app/data/services/notification_service.dart'; import 'package:suji/core/utils/date_utils.dart'; import 'package:suji/core/utils/logger.dart'; @@ -10,7 +12,7 @@ import 'package:suji/main.dart'; final ReceivePort port = ReceivePort(); -class BackgroundService { +class BackgroundService extends GetxService { static BackgroundService? _instance; static const String _isolateName = 'isolate'; static SendPort? _uiSendPort; @@ -24,19 +26,14 @@ class BackgroundService { IsolateNameServer.registerPortWithName(port.sendPort, _isolateName); } - static alarmFired() { - Log.d('[BackgroundService][alarmFired]', 'ALARM FIRED!!!'); - // print("ALARM FIRED!!!"); - } - @pragma('vm:entry-point') static Future callback() async { Log.d('[BackgroundService][callback]', 'CALLBACK FIRED 2!!!'); - // print("ALARM FIRED 2!!!"); final notificationService = NotificationService(); await notificationService.showNotification(flutterLocalNotificationsPlugin); - final ShalatLocalDataSourceImpl dataSource = ShalatLocalDataSourceImpl(); + final ShalatLocalDataSource dataSource = + ShalatLocalDataSourceImpl(databaseHelper: DatabaseHelper()); final shalatTime = await dataSource.getShalatTimeByDate(DateTime.now()); if (shalatTime != null) { await AndroidAlarmManager.oneShotAt( @@ -44,6 +41,7 @@ class BackgroundService { 0, BackgroundService.callback, exact: true, + rescheduleOnReboot: true, wakeup: true); } diff --git a/lib/app/data/services/location_service.dart b/lib/app/data/services/location_service.dart index 1018626..1021d8c 100644 --- a/lib/app/data/services/location_service.dart +++ b/lib/app/data/services/location_service.dart @@ -1,6 +1,7 @@ import 'package:geolocator/geolocator.dart'; +import 'package:get/get.dart'; -class LocationService { +class LocationService extends GetxService { static LocationService? _instance; LocationService._internal() { diff --git a/lib/app/data/services/notification_service.dart b/lib/app/data/services/notification_service.dart index 988e06a..9c5e0c0 100644 --- a/lib/app/data/services/notification_service.dart +++ b/lib/app/data/services/notification_service.dart @@ -1,5 +1,8 @@ +import 'dart:typed_data'; + import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; +import 'package:intl/intl.dart'; import 'package:rxdart/rxdart.dart'; import 'package:suji/core/utils/logger.dart'; @@ -29,11 +32,18 @@ class NotificationService { Future showNotification( FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin) async { - var androidPlatformChannelSpecifics = const AndroidNotificationDetails( + final Int64List vibrationPattern = Int64List(4); + vibrationPattern[0] = 0; + vibrationPattern[1] = 1000; + vibrationPattern[2] = 5000; + vibrationPattern[3] = 2000; + final androidPlatformChannelSpecifics = AndroidNotificationDetails( _channelId, _channelName, channelDescription: _channelDesc, importance: Importance.max, priority: Priority.high, + sound: const RawResourceAndroidNotificationSound('mecca_56_22'), + vibrationPattern: vibrationPattern, ticker: 'ticker'); var platformChannelSpecifics = NotificationDetails( @@ -43,7 +53,7 @@ class NotificationService { await flutterLocalNotificationsPlugin.show( 0, 'Adzan', - 'Waktunya Shalat {shalatTime}', + 'Waktunya Shalat ${DateFormat('HH:mm').format(DateTime.now())}', platformChannelSpecifics, payload: 'plain notification', ); diff --git a/lib/app/domain/entities/asmaul_husna.dart b/lib/app/domain/entities/asmaul_husna.dart new file mode 100644 index 0000000..1ee6e26 --- /dev/null +++ b/lib/app/domain/entities/asmaul_husna.dart @@ -0,0 +1,15 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'asmaul_husna.freezed.dart'; + +@freezed +class AsmaulHusna with _$AsmaulHusna { + const AsmaulHusna._(); + factory AsmaulHusna({ + required String arabic, + required String index, + required String latin, + required String translationEn, + required String translationId, + }) = _AsmaulHusna; +} diff --git a/lib/app/domain/entities/asmaul_husna.freezed.dart b/lib/app/domain/entities/asmaul_husna.freezed.dart new file mode 100644 index 0000000..79972e5 --- /dev/null +++ b/lib/app/domain/entities/asmaul_husna.freezed.dart @@ -0,0 +1,221 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'asmaul_husna.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$AsmaulHusna { + String get arabic => throw _privateConstructorUsedError; + String get index => throw _privateConstructorUsedError; + String get latin => throw _privateConstructorUsedError; + String get translationEn => throw _privateConstructorUsedError; + String get translationId => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $AsmaulHusnaCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AsmaulHusnaCopyWith<$Res> { + factory $AsmaulHusnaCopyWith( + AsmaulHusna value, $Res Function(AsmaulHusna) then) = + _$AsmaulHusnaCopyWithImpl<$Res, AsmaulHusna>; + @useResult + $Res call( + {String arabic, + String index, + String latin, + String translationEn, + String translationId}); +} + +/// @nodoc +class _$AsmaulHusnaCopyWithImpl<$Res, $Val extends AsmaulHusna> + implements $AsmaulHusnaCopyWith<$Res> { + _$AsmaulHusnaCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? arabic = null, + Object? index = null, + Object? latin = null, + Object? translationEn = null, + Object? translationId = null, + }) { + return _then(_value.copyWith( + arabic: null == arabic + ? _value.arabic + : arabic // ignore: cast_nullable_to_non_nullable + as String, + index: null == index + ? _value.index + : index // ignore: cast_nullable_to_non_nullable + as String, + latin: null == latin + ? _value.latin + : latin // ignore: cast_nullable_to_non_nullable + as String, + translationEn: null == translationEn + ? _value.translationEn + : translationEn // ignore: cast_nullable_to_non_nullable + as String, + translationId: null == translationId + ? _value.translationId + : translationId // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_AsmaulHusnaCopyWith<$Res> + implements $AsmaulHusnaCopyWith<$Res> { + factory _$$_AsmaulHusnaCopyWith( + _$_AsmaulHusna value, $Res Function(_$_AsmaulHusna) then) = + __$$_AsmaulHusnaCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String arabic, + String index, + String latin, + String translationEn, + String translationId}); +} + +/// @nodoc +class __$$_AsmaulHusnaCopyWithImpl<$Res> + extends _$AsmaulHusnaCopyWithImpl<$Res, _$_AsmaulHusna> + implements _$$_AsmaulHusnaCopyWith<$Res> { + __$$_AsmaulHusnaCopyWithImpl( + _$_AsmaulHusna _value, $Res Function(_$_AsmaulHusna) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? arabic = null, + Object? index = null, + Object? latin = null, + Object? translationEn = null, + Object? translationId = null, + }) { + return _then(_$_AsmaulHusna( + arabic: null == arabic + ? _value.arabic + : arabic // ignore: cast_nullable_to_non_nullable + as String, + index: null == index + ? _value.index + : index // ignore: cast_nullable_to_non_nullable + as String, + latin: null == latin + ? _value.latin + : latin // ignore: cast_nullable_to_non_nullable + as String, + translationEn: null == translationEn + ? _value.translationEn + : translationEn // ignore: cast_nullable_to_non_nullable + as String, + translationId: null == translationId + ? _value.translationId + : translationId // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$_AsmaulHusna extends _AsmaulHusna { + _$_AsmaulHusna( + {required this.arabic, + required this.index, + required this.latin, + required this.translationEn, + required this.translationId}) + : super._(); + + @override + final String arabic; + @override + final String index; + @override + final String latin; + @override + final String translationEn; + @override + final String translationId; + + @override + String toString() { + return 'AsmaulHusna(arabic: $arabic, index: $index, latin: $latin, translationEn: $translationEn, translationId: $translationId)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_AsmaulHusna && + (identical(other.arabic, arabic) || other.arabic == arabic) && + (identical(other.index, index) || other.index == index) && + (identical(other.latin, latin) || other.latin == latin) && + (identical(other.translationEn, translationEn) || + other.translationEn == translationEn) && + (identical(other.translationId, translationId) || + other.translationId == translationId)); + } + + @override + int get hashCode => Object.hash( + runtimeType, arabic, index, latin, translationEn, translationId); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_AsmaulHusnaCopyWith<_$_AsmaulHusna> get copyWith => + __$$_AsmaulHusnaCopyWithImpl<_$_AsmaulHusna>(this, _$identity); +} + +abstract class _AsmaulHusna extends AsmaulHusna { + factory _AsmaulHusna( + {required final String arabic, + required final String index, + required final String latin, + required final String translationEn, + required final String translationId}) = _$_AsmaulHusna; + _AsmaulHusna._() : super._(); + + @override + String get arabic; + @override + String get index; + @override + String get latin; + @override + String get translationEn; + @override + String get translationId; + @override + @JsonKey(ignore: true) + _$$_AsmaulHusnaCopyWith<_$_AsmaulHusna> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/app/domain/entities/daily_pray.dart b/lib/app/domain/entities/daily_pray.dart new file mode 100644 index 0000000..46499ce --- /dev/null +++ b/lib/app/domain/entities/daily_pray.dart @@ -0,0 +1,14 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'daily_pray.freezed.dart'; + +@freezed +class DailyPray with _$DailyPray { + const DailyPray._(); + factory DailyPray({ + required String title, + required String arabic, + required String latin, + required String translation, + }) = _DailyPray; +} diff --git a/lib/app/domain/entities/daily_pray.freezed.dart b/lib/app/domain/entities/daily_pray.freezed.dart new file mode 100644 index 0000000..f03433b --- /dev/null +++ b/lib/app/domain/entities/daily_pray.freezed.dart @@ -0,0 +1,190 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'daily_pray.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$DailyPray { + String get title => throw _privateConstructorUsedError; + String get arabic => throw _privateConstructorUsedError; + String get latin => throw _privateConstructorUsedError; + String get translation => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $DailyPrayCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $DailyPrayCopyWith<$Res> { + factory $DailyPrayCopyWith(DailyPray value, $Res Function(DailyPray) then) = + _$DailyPrayCopyWithImpl<$Res, DailyPray>; + @useResult + $Res call({String title, String arabic, String latin, String translation}); +} + +/// @nodoc +class _$DailyPrayCopyWithImpl<$Res, $Val extends DailyPray> + implements $DailyPrayCopyWith<$Res> { + _$DailyPrayCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? title = null, + Object? arabic = null, + Object? latin = null, + Object? translation = null, + }) { + return _then(_value.copyWith( + title: null == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String, + arabic: null == arabic + ? _value.arabic + : arabic // ignore: cast_nullable_to_non_nullable + as String, + latin: null == latin + ? _value.latin + : latin // ignore: cast_nullable_to_non_nullable + as String, + translation: null == translation + ? _value.translation + : translation // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_DailyPrayCopyWith<$Res> implements $DailyPrayCopyWith<$Res> { + factory _$$_DailyPrayCopyWith( + _$_DailyPray value, $Res Function(_$_DailyPray) then) = + __$$_DailyPrayCopyWithImpl<$Res>; + @override + @useResult + $Res call({String title, String arabic, String latin, String translation}); +} + +/// @nodoc +class __$$_DailyPrayCopyWithImpl<$Res> + extends _$DailyPrayCopyWithImpl<$Res, _$_DailyPray> + implements _$$_DailyPrayCopyWith<$Res> { + __$$_DailyPrayCopyWithImpl( + _$_DailyPray _value, $Res Function(_$_DailyPray) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? title = null, + Object? arabic = null, + Object? latin = null, + Object? translation = null, + }) { + return _then(_$_DailyPray( + title: null == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String, + arabic: null == arabic + ? _value.arabic + : arabic // ignore: cast_nullable_to_non_nullable + as String, + latin: null == latin + ? _value.latin + : latin // ignore: cast_nullable_to_non_nullable + as String, + translation: null == translation + ? _value.translation + : translation // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$_DailyPray extends _DailyPray { + _$_DailyPray( + {required this.title, + required this.arabic, + required this.latin, + required this.translation}) + : super._(); + + @override + final String title; + @override + final String arabic; + @override + final String latin; + @override + final String translation; + + @override + String toString() { + return 'DailyPray(title: $title, arabic: $arabic, latin: $latin, translation: $translation)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_DailyPray && + (identical(other.title, title) || other.title == title) && + (identical(other.arabic, arabic) || other.arabic == arabic) && + (identical(other.latin, latin) || other.latin == latin) && + (identical(other.translation, translation) || + other.translation == translation)); + } + + @override + int get hashCode => + Object.hash(runtimeType, title, arabic, latin, translation); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_DailyPrayCopyWith<_$_DailyPray> get copyWith => + __$$_DailyPrayCopyWithImpl<_$_DailyPray>(this, _$identity); +} + +abstract class _DailyPray extends DailyPray { + factory _DailyPray( + {required final String title, + required final String arabic, + required final String latin, + required final String translation}) = _$_DailyPray; + _DailyPray._() : super._(); + + @override + String get title; + @override + String get arabic; + @override + String get latin; + @override + String get translation; + @override + @JsonKey(ignore: true) + _$$_DailyPrayCopyWith<_$_DailyPray> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/app/domain/entities/surah_detail.dart b/lib/app/domain/entities/surah_detail.dart index 8d004b0..6a932a7 100644 --- a/lib/app/domain/entities/surah_detail.dart +++ b/lib/app/domain/entities/surah_detail.dart @@ -17,6 +17,7 @@ class SurahDetail with _$SurahDetail { required String nameTransliterationId, required String nameTranslationId, required String relevationId, + required String tafsirId, required String preBismillahTextArab, required String preBismillahAudioPrimary, required String versesTextArab, diff --git a/lib/app/domain/entities/surah_detail.freezed.dart b/lib/app/domain/entities/surah_detail.freezed.dart index 9862c6e..299ce53 100644 --- a/lib/app/domain/entities/surah_detail.freezed.dart +++ b/lib/app/domain/entities/surah_detail.freezed.dart @@ -27,6 +27,7 @@ mixin _$SurahDetail { String get nameTransliterationId => throw _privateConstructorUsedError; String get nameTranslationId => throw _privateConstructorUsedError; String get relevationId => throw _privateConstructorUsedError; + String get tafsirId => throw _privateConstructorUsedError; String get preBismillahTextArab => throw _privateConstructorUsedError; String get preBismillahAudioPrimary => throw _privateConstructorUsedError; String get versesTextArab => throw _privateConstructorUsedError; @@ -56,6 +57,7 @@ abstract class $SurahDetailCopyWith<$Res> { String nameTransliterationId, String nameTranslationId, String relevationId, + String tafsirId, String preBismillahTextArab, String preBismillahAudioPrimary, String versesTextArab, @@ -86,6 +88,7 @@ class _$SurahDetailCopyWithImpl<$Res, $Val extends SurahDetail> Object? nameTransliterationId = null, Object? nameTranslationId = null, Object? relevationId = null, + Object? tafsirId = null, Object? preBismillahTextArab = null, Object? preBismillahAudioPrimary = null, Object? versesTextArab = null, @@ -124,6 +127,10 @@ class _$SurahDetailCopyWithImpl<$Res, $Val extends SurahDetail> ? _value.relevationId : relevationId // ignore: cast_nullable_to_non_nullable as String, + tafsirId: null == tafsirId + ? _value.tafsirId + : tafsirId // ignore: cast_nullable_to_non_nullable + as String, preBismillahTextArab: null == preBismillahTextArab ? _value.preBismillahTextArab : preBismillahTextArab // ignore: cast_nullable_to_non_nullable @@ -176,6 +183,7 @@ abstract class _$$_SurahDetailCopyWith<$Res> String nameTransliterationId, String nameTranslationId, String relevationId, + String tafsirId, String preBismillahTextArab, String preBismillahAudioPrimary, String versesTextArab, @@ -204,6 +212,7 @@ class __$$_SurahDetailCopyWithImpl<$Res> Object? nameTransliterationId = null, Object? nameTranslationId = null, Object? relevationId = null, + Object? tafsirId = null, Object? preBismillahTextArab = null, Object? preBismillahAudioPrimary = null, Object? versesTextArab = null, @@ -242,6 +251,10 @@ class __$$_SurahDetailCopyWithImpl<$Res> ? _value.relevationId : relevationId // ignore: cast_nullable_to_non_nullable as String, + tafsirId: null == tafsirId + ? _value.tafsirId + : tafsirId // ignore: cast_nullable_to_non_nullable + as String, preBismillahTextArab: null == preBismillahTextArab ? _value.preBismillahTextArab : preBismillahTextArab // ignore: cast_nullable_to_non_nullable @@ -289,6 +302,7 @@ class _$_SurahDetail extends _SurahDetail { required this.nameTransliterationId, required this.nameTranslationId, required this.relevationId, + required this.tafsirId, required this.preBismillahTextArab, required this.preBismillahAudioPrimary, required this.versesTextArab, @@ -317,6 +331,8 @@ class _$_SurahDetail extends _SurahDetail { @override final String relevationId; @override + final String tafsirId; + @override final String preBismillahTextArab; @override final String preBismillahAudioPrimary; @@ -335,7 +351,7 @@ class _$_SurahDetail extends _SurahDetail { @override String toString() { - return 'SurahDetail(number: $number, numberOfVerses: $numberOfVerses, nameShort: $nameShort, nameLong: $nameLong, nameTransliterationId: $nameTransliterationId, nameTranslationId: $nameTranslationId, relevationId: $relevationId, preBismillahTextArab: $preBismillahTextArab, preBismillahAudioPrimary: $preBismillahAudioPrimary, versesTextArab: $versesTextArab, versesNumberInQuran: $versesNumberInQuran, versesNumberInSurah: $versesNumberInSurah, versesTranslationId: $versesTranslationId, versesAudioPrimary: $versesAudioPrimary, versesTafsirIdShort: $versesTafsirIdShort)'; + return 'SurahDetail(number: $number, numberOfVerses: $numberOfVerses, nameShort: $nameShort, nameLong: $nameLong, nameTransliterationId: $nameTransliterationId, nameTranslationId: $nameTranslationId, relevationId: $relevationId, tafsirId: $tafsirId, preBismillahTextArab: $preBismillahTextArab, preBismillahAudioPrimary: $preBismillahAudioPrimary, versesTextArab: $versesTextArab, versesNumberInQuran: $versesNumberInQuran, versesNumberInSurah: $versesNumberInSurah, versesTranslationId: $versesTranslationId, versesAudioPrimary: $versesAudioPrimary, versesTafsirIdShort: $versesTafsirIdShort)'; } @override @@ -356,6 +372,8 @@ class _$_SurahDetail extends _SurahDetail { other.nameTranslationId == nameTranslationId) && (identical(other.relevationId, relevationId) || other.relevationId == relevationId) && + (identical(other.tafsirId, tafsirId) || + other.tafsirId == tafsirId) && (identical(other.preBismillahTextArab, preBismillahTextArab) || other.preBismillahTextArab == preBismillahTextArab) && (identical( @@ -386,6 +404,7 @@ class _$_SurahDetail extends _SurahDetail { nameTransliterationId, nameTranslationId, relevationId, + tafsirId, preBismillahTextArab, preBismillahAudioPrimary, versesTextArab, @@ -418,6 +437,7 @@ abstract class _SurahDetail extends SurahDetail { required final String nameTransliterationId, required final String nameTranslationId, required final String relevationId, + required final String tafsirId, required final String preBismillahTextArab, required final String preBismillahAudioPrimary, required final String versesTextArab, @@ -446,6 +466,8 @@ abstract class _SurahDetail extends SurahDetail { @override String get relevationId; @override + String get tafsirId; + @override String get preBismillahTextArab; @override String get preBismillahAudioPrimary; diff --git a/lib/app/domain/entities/surah_detail.g.dart b/lib/app/domain/entities/surah_detail.g.dart index c9d349e..63abefc 100644 --- a/lib/app/domain/entities/surah_detail.g.dart +++ b/lib/app/domain/entities/surah_detail.g.dart @@ -62,33 +62,38 @@ const SurahDetailSchema = CollectionSchema( name: r'relevationId', type: IsarType.string, ), - r'versesAudioPrimary': PropertySchema( + r'tafsirId': PropertySchema( id: 9, + name: r'tafsirId', + type: IsarType.string, + ), + r'versesAudioPrimary': PropertySchema( + id: 10, name: r'versesAudioPrimary', type: IsarType.string, ), r'versesNumberInQuran': PropertySchema( - id: 10, + id: 11, name: r'versesNumberInQuran', type: IsarType.long, ), r'versesNumberInSurah': PropertySchema( - id: 11, + id: 12, name: r'versesNumberInSurah', type: IsarType.long, ), r'versesTafsirIdShort': PropertySchema( - id: 12, + id: 13, name: r'versesTafsirIdShort', type: IsarType.string, ), r'versesTextArab': PropertySchema( - id: 13, + id: 14, name: r'versesTextArab', type: IsarType.string, ), r'versesTranslationId': PropertySchema( - id: 14, + id: 15, name: r'versesTranslationId', type: IsarType.string, ) @@ -120,6 +125,7 @@ int _surahDetailEstimateSize( bytesCount += 3 + object.preBismillahAudioPrimary.length * 3; bytesCount += 3 + object.preBismillahTextArab.length * 3; bytesCount += 3 + object.relevationId.length * 3; + bytesCount += 3 + object.tafsirId.length * 3; bytesCount += 3 + object.versesAudioPrimary.length * 3; bytesCount += 3 + object.versesTafsirIdShort.length * 3; bytesCount += 3 + object.versesTextArab.length * 3; @@ -142,12 +148,13 @@ void _surahDetailSerialize( writer.writeString(offsets[6], object.preBismillahAudioPrimary); writer.writeString(offsets[7], object.preBismillahTextArab); writer.writeString(offsets[8], object.relevationId); - writer.writeString(offsets[9], object.versesAudioPrimary); - writer.writeLong(offsets[10], object.versesNumberInQuran); - writer.writeLong(offsets[11], object.versesNumberInSurah); - writer.writeString(offsets[12], object.versesTafsirIdShort); - writer.writeString(offsets[13], object.versesTextArab); - writer.writeString(offsets[14], object.versesTranslationId); + writer.writeString(offsets[9], object.tafsirId); + writer.writeString(offsets[10], object.versesAudioPrimary); + writer.writeLong(offsets[11], object.versesNumberInQuran); + writer.writeLong(offsets[12], object.versesNumberInSurah); + writer.writeString(offsets[13], object.versesTafsirIdShort); + writer.writeString(offsets[14], object.versesTextArab); + writer.writeString(offsets[15], object.versesTranslationId); } SurahDetail _surahDetailDeserialize( @@ -166,12 +173,13 @@ SurahDetail _surahDetailDeserialize( preBismillahAudioPrimary: reader.readString(offsets[6]), preBismillahTextArab: reader.readString(offsets[7]), relevationId: reader.readString(offsets[8]), - versesAudioPrimary: reader.readString(offsets[9]), - versesNumberInQuran: reader.readLong(offsets[10]), - versesNumberInSurah: reader.readLong(offsets[11]), - versesTafsirIdShort: reader.readString(offsets[12]), - versesTextArab: reader.readString(offsets[13]), - versesTranslationId: reader.readString(offsets[14]), + tafsirId: reader.readString(offsets[9]), + versesAudioPrimary: reader.readString(offsets[10]), + versesNumberInQuran: reader.readLong(offsets[11]), + versesNumberInSurah: reader.readLong(offsets[12]), + versesTafsirIdShort: reader.readString(offsets[13]), + versesTextArab: reader.readString(offsets[14]), + versesTranslationId: reader.readString(offsets[15]), ); return object; } @@ -204,15 +212,17 @@ P _surahDetailDeserializeProp

( case 9: return (reader.readString(offset)) as P; case 10: - return (reader.readLong(offset)) as P; + return (reader.readString(offset)) as P; case 11: return (reader.readLong(offset)) as P; case 12: - return (reader.readString(offset)) as P; + return (reader.readLong(offset)) as P; case 13: return (reader.readString(offset)) as P; case 14: return (reader.readString(offset)) as P; + case 15: + return (reader.readString(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -1426,6 +1436,141 @@ extension SurahDetailQueryFilter }); } + QueryBuilder tafsirIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'tafsirId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + tafsirIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'tafsirId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + tafsirIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'tafsirId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder tafsirIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'tafsirId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + tafsirIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'tafsirId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + tafsirIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'tafsirId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + tafsirIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'tafsirId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder tafsirIdMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'tafsirId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + tafsirIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'tafsirId', + value: '', + )); + }); + } + + QueryBuilder + tafsirIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'tafsirId', + value: '', + )); + }); + } + QueryBuilder versesAudioPrimaryEqualTo( String value, { @@ -2209,6 +2354,18 @@ extension SurahDetailQuerySortBy }); } + QueryBuilder sortByTafsirId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'tafsirId', Sort.asc); + }); + } + + QueryBuilder sortByTafsirIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'tafsirId', Sort.desc); + }); + } + QueryBuilder sortByVersesAudioPrimary() { return QueryBuilder.apply(this, (query) { @@ -2425,6 +2582,18 @@ extension SurahDetailQuerySortThenBy }); } + QueryBuilder thenByTafsirId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'tafsirId', Sort.asc); + }); + } + + QueryBuilder thenByTafsirIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'tafsirId', Sort.desc); + }); + } + QueryBuilder thenByVersesAudioPrimary() { return QueryBuilder.apply(this, (query) { @@ -2576,6 +2745,13 @@ extension SurahDetailQueryWhereDistinct }); } + QueryBuilder distinctByTafsirId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'tafsirId', caseSensitive: caseSensitive); + }); + } + QueryBuilder distinctByVersesAudioPrimary({bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { @@ -2689,6 +2865,12 @@ extension SurahDetailQueryProperty }); } + QueryBuilder tafsirIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'tafsirId'); + }); + } + QueryBuilder versesAudioPrimaryProperty() { return QueryBuilder.apply(this, (query) { @@ -2744,6 +2926,7 @@ _$_SurahDetail _$$_SurahDetailFromJson(Map json) => nameTransliterationId: json['nameTransliterationId'] as String, nameTranslationId: json['nameTranslationId'] as String, relevationId: json['relevationId'] as String, + tafsirId: json['tafsirId'] as String, preBismillahTextArab: json['preBismillahTextArab'] as String, preBismillahAudioPrimary: json['preBismillahAudioPrimary'] as String, versesTextArab: json['versesTextArab'] as String, @@ -2763,6 +2946,7 @@ Map _$$_SurahDetailToJson(_$_SurahDetail instance) => 'nameTransliterationId': instance.nameTransliterationId, 'nameTranslationId': instance.nameTranslationId, 'relevationId': instance.relevationId, + 'tafsirId': instance.tafsirId, 'preBismillahTextArab': instance.preBismillahTextArab, 'preBismillahAudioPrimary': instance.preBismillahAudioPrimary, 'versesTextArab': instance.versesTextArab, diff --git a/lib/app/domain/repository/shalat_repository.dart b/lib/app/domain/repository/shalat_repository.dart index 666b8a0..413dc15 100644 --- a/lib/app/domain/repository/shalat_repository.dart +++ b/lib/app/domain/repository/shalat_repository.dart @@ -4,6 +4,6 @@ import 'package:suji/app/domain/entities/shalat.dart'; import 'package:suji/core/utils/failure.dart'; abstract class ShalatRepository { - Future> getShalatTime(Position position); - Future fetchShalatTime(Position position); + Future> getShalatTime( + DateTime dateTime, Position position); } diff --git a/lib/app/domain/repository/surah_repository.dart b/lib/app/domain/repository/surah_repository.dart index 1882551..a3705c0 100644 --- a/lib/app/domain/repository/surah_repository.dart +++ b/lib/app/domain/repository/surah_repository.dart @@ -1,4 +1,6 @@ import 'package:dartz/dartz.dart'; +import 'package:suji/app/domain/entities/asmaul_husna.dart'; +import 'package:suji/app/domain/entities/daily_pray.dart'; import 'package:suji/app/domain/entities/surah.dart'; import 'package:suji/app/domain/entities/surah_detail.dart'; import 'package:suji/core/utils/failure.dart'; @@ -8,7 +10,6 @@ abstract class SurahRepository { Future>> getSurahByQuery(String query); Future>> getAyahBySurahNumber( int surahNumber); - Future> insertOrUpdateSurah(List listSurah); - Future> insertOrUpdateAyah( - List listAyah); + Future>> getDailyPray(); + Future>> getAsmaulHusna(); } diff --git a/lib/app/domain/usescases/get_all_surah_usecase.dart b/lib/app/domain/usescases/get_all_surah_usecase.dart new file mode 100644 index 0000000..881b906 --- /dev/null +++ b/lib/app/domain/usescases/get_all_surah_usecase.dart @@ -0,0 +1,13 @@ +import 'package:dartz/dartz.dart'; +import 'package:suji/app/domain/entities/surah.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/core/utils/failure.dart'; + +class GetAllSurahUsecase { + final SurahRepository surahRepository; + GetAllSurahUsecase({required this.surahRepository}); + + Future>> invoke() { + return surahRepository.getAllSurah(); + } +} diff --git a/lib/app/domain/usescases/get_asmaul_husna_usecase.dart b/lib/app/domain/usescases/get_asmaul_husna_usecase.dart new file mode 100644 index 0000000..b249520 --- /dev/null +++ b/lib/app/domain/usescases/get_asmaul_husna_usecase.dart @@ -0,0 +1,14 @@ +import 'package:dartz/dartz.dart'; +import 'package:suji/app/domain/entities/asmaul_husna.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/core/utils/failure.dart'; + +class GetAsmaulHusnaUsecase { + final SurahRepository surahRepository; + + GetAsmaulHusnaUsecase({required this.surahRepository}); + + Future>> invoke() { + return surahRepository.getAsmaulHusna(); + } +} diff --git a/lib/app/domain/usescases/get_ayah_by_surah_number_usecase.dart b/lib/app/domain/usescases/get_ayah_by_surah_number_usecase.dart new file mode 100644 index 0000000..f121119 --- /dev/null +++ b/lib/app/domain/usescases/get_ayah_by_surah_number_usecase.dart @@ -0,0 +1,12 @@ +import 'package:dartz/dartz.dart'; +import 'package:suji/app/domain/entities/surah_detail.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/core/utils/failure.dart'; + +class GetAyahBySurahNumberUsecase { + final SurahRepository surahRepository; + GetAyahBySurahNumberUsecase({required this.surahRepository}); + Future>> invoke(int surahNumber) { + return surahRepository.getAyahBySurahNumber(surahNumber); + } +} diff --git a/lib/app/domain/usescases/get_daily_pray_usecase.dart b/lib/app/domain/usescases/get_daily_pray_usecase.dart new file mode 100644 index 0000000..334b0e0 --- /dev/null +++ b/lib/app/domain/usescases/get_daily_pray_usecase.dart @@ -0,0 +1,13 @@ +import 'package:dartz/dartz.dart'; +import 'package:suji/app/domain/entities/daily_pray.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/core/utils/failure.dart'; + +class GetDailyPrayUsecase { + final SurahRepository surahRepository; + GetDailyPrayUsecase({required this.surahRepository}); + + Future>> invoke() { + return surahRepository.getDailyPray(); + } +} diff --git a/lib/app/domain/usescases/get_shalat_time_usecase.dart b/lib/app/domain/usescases/get_shalat_time_usecase.dart new file mode 100644 index 0000000..fe92ff1 --- /dev/null +++ b/lib/app/domain/usescases/get_shalat_time_usecase.dart @@ -0,0 +1,14 @@ +import 'package:dartz/dartz.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:suji/app/domain/entities/shalat.dart'; +import 'package:suji/app/domain/repository/shalat_repository.dart'; +import 'package:suji/core/utils/failure.dart'; + +class GetShalatTimeUsecase { + final ShalatRepository shalatRepository; + GetShalatTimeUsecase({required this.shalatRepository}); + + Future> invoke(DateTime dateTime, Position position) { + return shalatRepository.getShalatTime(dateTime, position); + } +} diff --git a/lib/app/domain/usescases/get_surah_by_query_usecase.dart b/lib/app/domain/usescases/get_surah_by_query_usecase.dart new file mode 100644 index 0000000..d4fcb37 --- /dev/null +++ b/lib/app/domain/usescases/get_surah_by_query_usecase.dart @@ -0,0 +1,14 @@ +import 'package:dartz/dartz.dart'; +import 'package:suji/app/domain/entities/surah.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/core/utils/failure.dart'; + +class GetSurahByQueryUsecase { + final SurahRepository surahRepository; + + GetSurahByQueryUsecase({required this.surahRepository}); + + Future>> invoke(String query) { + return surahRepository.getSurahByQuery(query); + } +} diff --git a/lib/app/modules/asmaul_husna/bindings/asmaul_husna_binding.dart b/lib/app/modules/asmaul_husna/bindings/asmaul_husna_binding.dart new file mode 100644 index 0000000..b1a1d81 --- /dev/null +++ b/lib/app/modules/asmaul_husna/bindings/asmaul_husna_binding.dart @@ -0,0 +1,14 @@ +import 'package:get/get.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_asmaul_husna_usecase.dart'; +import 'package:suji/app/modules/asmaul_husna/controllers/asmaul_husna_controller.dart'; + +class AsmaulHusnaBinding implements Bindings { + @override + void dependencies() { + Get.lazyPut(() => AsmaulHusnaController( + getAsmaulHusnaUsecase: Get.find())); + Get.lazyPut(() => + GetAsmaulHusnaUsecase(surahRepository: Get.find())); + } +} diff --git a/lib/app/modules/asmaul_husna/controllers/asmaul_husna_controller.dart b/lib/app/modules/asmaul_husna/controllers/asmaul_husna_controller.dart new file mode 100644 index 0000000..5844a0d --- /dev/null +++ b/lib/app/modules/asmaul_husna/controllers/asmaul_husna_controller.dart @@ -0,0 +1,43 @@ +import 'package:get/get.dart'; +import 'package:suji/app/domain/entities/asmaul_husna.dart'; +import 'package:suji/app/domain/usescases/get_asmaul_husna_usecase.dart'; +import 'package:suji/app/widgets/custom_snackbar.dart'; + +class AsmaulHusnaController extends GetxController + with StateMixin> { + final GetAsmaulHusnaUsecase getAsmaulHusnaUsecase; + AsmaulHusnaController({required this.getAsmaulHusnaUsecase}); + + // final _state = BaseState.empty.obs; + // get state => _state.value; + + // final _message = ''.obs; + // String get message => _message.value; + + // final RxList _dataAsmaulHusna = [].obs; + // get dataAsmaulHusna => _dataAsmaulHusna; + + @override + void onInit() async { + super.onInit(); + await getAsmaulHusna(); + } + + /// get data asmaul husna from repository + Future getAsmaulHusna() async { + // _state.value = BaseState.loading; + change([], status: RxStatus.loading()); + final result = await getAsmaulHusnaUsecase.invoke(); + + result.fold((failure) { + // _message.value = l.toString(); + // _state.value = BaseState.error; + showErrorMessage(failure.message); + change([], status: RxStatus.error(failure.message)); + }, (result) { + // _dataAsmaulHusna.assignAll(r); + // _state.value = BaseState.success; + change(result, status: RxStatus.success()); + }); + } +} diff --git a/lib/app/modules/asmaul_husna/views/asmaul_husna_view.dart b/lib/app/modules/asmaul_husna/views/asmaul_husna_view.dart new file mode 100644 index 0000000..1fb7593 --- /dev/null +++ b/lib/app/modules/asmaul_husna/views/asmaul_husna_view.dart @@ -0,0 +1,297 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:suji/app/modules/asmaul_husna/controllers/asmaul_husna_controller.dart'; +import 'package:suji/app/widgets/box_placeholder.dart'; +import 'package:suji/core/theme/colors.dart'; +import 'package:suji/core/values/constant.dart'; + +class AsmaulHusnaView extends GetView { + const AsmaulHusnaView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text( + AppString.asmaulHusna, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + leading: IconButton( + onPressed: () => Get.back(), + icon: const Icon( + Icons.arrow_back_ios_new, + color: AppColors.background, + ), + ), + ), + body: SafeArea( + child: controller.obx( + _buildAsmaulHusnaSuccess, + onLoading: _buildAsmaulHusnaLoading(), + onError: _buildAsmaulHusnaError, + onEmpty: const SizedBox.shrink(), + ), + // Obx(() { + // if (controller.state == BaseState.loading) { + // return GridView.builder( + // padding: EdgeInsets.zero, + // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + // childAspectRatio: 1.0, + // crossAxisCount: 3, + // mainAxisSpacing: 6, + // crossAxisSpacing: 6, + // ), + // itemCount: 9, + // shrinkWrap: true, + // physics: const ScrollPhysics(), + // itemBuilder: (BuildContext context, int index) { + // return const BoxPlaceholder( + // width: 64, + // height: 64, + // ); + // }, + // ); + // } + // if (controller.state == BaseState.error) { + // Get.snackbar('information'.tr, controller.message, + // snackPosition: SnackPosition.BOTTOM, + // margin: const EdgeInsets.all(8.0), + // backgroundColor: AppColors.error); + // return Center( + // child: IconButton( + // onPressed: () async => await controller.getAsmaulHusna(), + // icon: const Icon( + // Icons.replay, + // size: 24.0, + // ), + // ), + // ); + // } + // if (controller.state == BaseState.success) { + // return Directionality( + // textDirection: TextDirection.rtl, + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: GridView.builder( + // padding: EdgeInsets.zero, + // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + // childAspectRatio: 1.0, + // crossAxisCount: 3, + // mainAxisSpacing: 6, + // crossAxisSpacing: 6, + // ), + // itemCount: controller.dataAsmaulHusna.length, + // shrinkWrap: true, + // physics: const ScrollPhysics(), + // itemBuilder: (BuildContext context, int index) { + // final asmaulHusna = controller.dataAsmaulHusna[index]; + // return Stack( + // children: [ + // Container( + // width: double.infinity, + // clipBehavior: Clip.hardEdge, + // decoration: BoxDecoration( + // color: AppColors.primary, + // borderRadius: BorderRadius.circular(12), + // boxShadow: const [ + // BoxShadow( + // blurRadius: 4.0, + // color: Colors.black, + // offset: Offset(0.0, 0.5)) + // ]), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.stretch, + // mainAxisAlignment: MainAxisAlignment.center, + + // children: [ + // Expanded( + // flex: 3, + // child: Container( + // color: Colors.grey[100], + // alignment: Alignment.bottomCenter, + // child: AutoSizeText( + // asmaulHusna.arabic, + // maxLines: 2, + // textAlign: TextAlign.center, + // style: Get.textTheme.displayLarge?.copyWith( + // color: AppColors.primary, + // ), + // ), + // ), + // ), + // Expanded( + // child: Align( + // alignment: Alignment.center, + // child: AutoSizeText(asmaulHusna.latin, + // maxLines: 2, + // textAlign: TextAlign.center, + // style: Get.textTheme.bodyMedium?.copyWith( + // color: Colors.white, + // )), + // ), + // ), + // ], + // ), + // ), + // Container( + // padding: const EdgeInsets.all(8.0), + // decoration: const BoxDecoration( + // color: AppColors.primary, + // borderRadius: BorderRadius.all( + // Radius.circular( + // 8.0, + // ), + // ), + // ), + // child: Text( + // asmaulHusna.index, + // style: const TextStyle( + // fontSize: 16.0, + // color: Colors.white, + // fontWeight: FontWeight.bold, + // ), + // ), + // ), + // ], + // ); + // }, + // ), + // ), + // ); + // } else { + // return const SizedBox.shrink(); + // } + // }), + ), + ); + } + + Widget _buildAsmaulHusnaError(errorMessage) { + return Center( + child: IconButton( + onPressed: () async => await controller.getAsmaulHusna(), + icon: const Icon( + Icons.replay, + size: 24.0, + ), + ), + ); + } + + GridView _buildAsmaulHusnaLoading() { + return GridView.builder( + padding: EdgeInsets.zero, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 1.0, + crossAxisCount: 3, + mainAxisSpacing: 6, + crossAxisSpacing: 6, + ), + itemCount: 9, + shrinkWrap: true, + physics: const ScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return const BoxPlaceholder( + width: 64, + height: 64, + ); + }, + ); + } + + Widget _buildAsmaulHusnaSuccess(state) { + return Directionality( + textDirection: TextDirection.rtl, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: GridView.builder( + padding: EdgeInsets.zero, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 1.0, + crossAxisCount: 3, + mainAxisSpacing: 6, + crossAxisSpacing: 6, + ), + itemCount: state?.length, + shrinkWrap: true, + physics: const ScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + final asmaulHusna = state![index]; + return Stack( + children: [ + Container( + width: double.infinity, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: AppColors.primary, + borderRadius: BorderRadius.circular(12), + boxShadow: const [ + BoxShadow( + blurRadius: 4.0, + color: Colors.black, + offset: Offset(0.0, 0.5)) + ]), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 3, + child: Container( + color: Colors.grey[100], + alignment: Alignment.bottomCenter, + child: AutoSizeText( + asmaulHusna.arabic, + maxLines: 2, + textAlign: TextAlign.center, + style: Get.textTheme.displayLarge?.copyWith( + color: AppColors.primary, + ), + ), + ), + ), + Expanded( + child: Align( + alignment: Alignment.center, + child: AutoSizeText(asmaulHusna.latin, + maxLines: 2, + textAlign: TextAlign.center, + style: Get.textTheme.bodyMedium?.copyWith( + color: Colors.white, + )), + ), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(8.0), + decoration: const BoxDecoration( + color: AppColors.primary, + borderRadius: BorderRadius.all( + Radius.circular( + 8.0, + ), + ), + ), + child: Text( + asmaulHusna.index, + style: const TextStyle( + fontSize: 16.0, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/app/modules/home/bindings/home_binding.dart b/lib/app/modules/home/bindings/home_binding.dart index 6563d20..5c4236a 100644 --- a/lib/app/modules/home/bindings/home_binding.dart +++ b/lib/app/modules/home/bindings/home_binding.dart @@ -1,9 +1,6 @@ import 'package:get/get.dart'; -import 'package:suji/app/data/providers/database_helper.dart'; -import 'package:suji/app/data/providers/surah_local_data_source.dart'; -import 'package:suji/app/data/providers/surah_remote_data_source.dart'; -import 'package:suji/app/data/repository/surah_repository_impl.dart'; import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_all_surah_usecase.dart'; import '../controllers/home_controller.dart'; @@ -11,11 +8,9 @@ class HomeBinding extends Bindings { @override void dependencies() { Get.lazyPut( - () => HomeController(), + () => HomeController(getAllSurahUsecase: Get.find()), ); - Get.lazyPut(() => SurahRepositoryImpl()); - Get.lazyPut(() => SurahRemoteDataSourceImpl()); - Get.lazyPut(() => SurahLocalDataSourceImpl()); - Get.lazyPut(() => DatabaseHelper()); + Get.lazyPut( + () => GetAllSurahUsecase(surahRepository: Get.find())); } } diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 5bde8b2..976ea4b 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -1,48 +1,64 @@ -import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:suji/app/data/services/background_service.dart'; -import 'package:suji/app/data/services/notification_service.dart'; +import 'package:hive/hive.dart'; import 'package:suji/app/domain/entities/surah.dart'; -import 'package:suji/app/domain/repository/surah_repository.dart'; -import 'package:suji/core/theme/colors.dart'; -import 'package:suji/core/utils/base_state.dart'; +import 'package:suji/app/domain/usescases/get_all_surah_usecase.dart'; +import 'package:suji/app/widgets/custom_snackbar.dart'; -class HomeController extends GetxController { - final SurahRepository surahRepository = Get.find(); +class HomeController extends GetxController with StateMixin> { + final GetAllSurahUsecase getAllSurahUsecase; + Box box = Hive.box('sujiSettingsBox'); - final _state = BaseState.empty.obs; - BaseState get state => _state.value; + HomeController({required this.getAllSurahUsecase}); - final RxList _listSurah = [].obs; - List get listSurah => _listSurah; + // final _state = BaseState.empty.obs; + // BaseState get state => _state.value; + + // final _message = ''.obs; + // String get message => _message.value; + + // final RxList _listSurah = [].obs; + // List get listSurah => _listSurah; + + final _lastRead = ''.obs; + String get lastRead => _lastRead.value; @override - void onInit() { + void onInit() async { super.onInit(); - getAllSurah(); - NotificationService.configureSelectNotificationSubject(); - port.listen((_) => BackgroundService.alarmFired()); + await getAllSurah(); + _lastRead.value = box.get('lastRead') ?? '-'; } - @override - void onClose() { - selectNotificationSubject.close(); - super.onClose(); + toSearchPage() async { + await Get.toNamed('/search'); + _getLastRead(); + } + + /// set last read into Hive Box + void setLastRead(String surahName) { + box.put('lastRead', surahName); + _getLastRead(); + } + + void _getLastRead() { + _lastRead.value = box.get('lastRead'); } + /// get all surah Future getAllSurah() async { - _state.value = BaseState.loading; - final result = await surahRepository.getAllSurah(); + // _state.value = BaseState.loading; + change([], status: RxStatus.loading()); + final result = await getAllSurahUsecase.invoke(); result.fold((failure) { - _state.value = BaseState.error; - Get.snackbar('Information', failure.message, - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error); + // _message.value = failure.message; + // _state.value = BaseState.error; + showErrorMessage(failure.message); + change([], status: RxStatus.error(failure.message)); }, (surahData) { - _listSurah.assignAll(surahData); - _state.value = BaseState.success; + // _listSurah.assignAll(surahData); + // _state.value = BaseState.success; + change(surahData, status: RxStatus.success()); }); } } diff --git a/lib/app/modules/home/views/home_view.dart b/lib/app/modules/home/views/home_view.dart index 1529768..44e0f2f 100644 --- a/lib/app/modules/home/views/home_view.dart +++ b/lib/app/modules/home/views/home_view.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:suji/app/domain/entities/surah.dart'; import 'package:suji/app/routes/app_pages.dart'; import 'package:suji/core/theme/colors.dart'; import 'package:suji/core/theme/text_style.dart'; -import 'package:suji/core/utils/base_state.dart'; import 'package:suji/app/widgets/box_placeholder.dart'; -import 'package:suji/core/utils/logger.dart'; import 'package:suji/core/values/constant.dart'; import '../controllers/home_controller.dart'; @@ -14,8 +13,35 @@ class HomeView extends GetView { const HomeView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - final TextTheme textTheme = Theme.of(context).textTheme; return Scaffold( + appBar: AppBar( + title: const Text( + AppString.alQuran, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + leading: IconButton( + onPressed: () => Get.back(), + icon: const Icon( + Icons.arrow_back_ios_new, + color: AppColors.background, + size: 24.0, + ), + ), + actions: [ + IconButton( + onPressed: () => controller.toSearchPage(), + tooltip: 'search'.tr, + icon: const Icon( + Icons.search, + color: Colors.white, + // size: 24.0, + ), + ), + ], + ), body: SafeArea( child: SingleChildScrollView( controller: ScrollController(), @@ -24,55 +50,54 @@ class HomeView extends GetView { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - IconButton( - onPressed: () => Get.toNamed('/shalat'), - tooltip: 'Jadwal Shalat', - icon: const Icon( - Icons.mosque_outlined, - size: 24.0, - ), - ), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text( - 'Suka Ngaji App', - style: TextStyle( - color: Color(0xFF672CBC), - fontSize: 24.0, - fontWeight: FontWeight.bold, - ), - ), - ), - const Spacer(), - IconButton( - onPressed: () => Get.toNamed('/search'), - tooltip: 'Cari', - icon: const Icon( - Icons.search, - size: 24.0, - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('greeting'.tr, - style: textTheme.headlineMedium - ?.copyWith(fontWeight: medium)), - Text( - 'user'.tr, - style: textTheme.displayMedium, - ), - ], - ), - ), + // Row( + // children: [ + // IconButton( + // onPressed: () => Get.back(), + // tooltip: 'back'.tr, + // icon: const Icon( + // Icons.arrow_back_ios, + // size: 24.0, + // ), + // ), + // const Padding( + // padding: EdgeInsets.all(8.0), + // child: Text( + // 'Suka Ngaji App', + // style: TextStyle( + // color: Color(0xFF672CBC), + // fontSize: 24.0, + // fontWeight: FontWeight.bold, + // ), + // ), + // ), + // const Spacer(), + // IconButton( + // onPressed: () => Get.toNamed('/search'), + // tooltip: 'Cari', + // icon: const Icon( + // Icons.search, + // size: 24.0, + // ), + // ), + // ], + // ), + // Padding( + // padding: const EdgeInsets.all(8.0), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text('greeting'.tr, + // style: Get.textTheme.headlineMedium + // ?.copyWith(fontWeight: medium)), + // Text( + // 'user'.tr, + // style: Get.textTheme.displayMedium, + // ), + // ], + // ), + // ), _buildHomeBanner(), - // _buildProgresJuz(), _buildSurahList() ], ), @@ -82,147 +107,143 @@ class HomeView extends GetView { ); } - Widget _buildSurahList() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Obx(() { - if (controller.state == BaseState.loading) { - // print('current State: ${controller.state}'); - Log.d('[HomeView][controller.state]', controller.state.toString()); - return ListView.separated( - itemCount: 10, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (BuildContext context, int index) { - return Container( - height: 4.0, - ); - }, - itemBuilder: (BuildContext context, int index) { - return const BoxPlaceholder(width: double.infinity, height: 64.0); - }, - ); - } else if (controller.state == BaseState.error) { - return Center( - child: IconButton( - onPressed: () async { - await controller.getAllSurah(); - }, - icon: const Icon( - Icons.replay, - size: 24.0, - ), - ), - ); - } else if (controller.state == BaseState.success) { - // print('Success: ${controller.listSurah.length}'); - Log.d('[HomeView][controller.listSurah.length]', - controller.listSurah.length.toString()); - return ListView.builder( - itemCount: controller.listSurah.length, - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - final surah = controller.listSurah[index]; - return InkWell( - onTap: () => Get.toNamed('${Routes.SURAH}/${surah.number}'), - child: Card( - child: ListTile( - leading: CircleAvatar( - backgroundColor: Colors.transparent, - backgroundImage: - const AssetImage(AppAssets.imgBorderSurah), - child: Text( - '${surah.number}', - style: textTheme.titleLarge, - ), - ), - title: Text(surah.nameTransliterationId, style: textTheme.titleLarge), - subtitle: Text( - '${surah.revelationId} - ${surah.numberOfVerses} Ayat'), - trailing: Text( - surah.nameShort, - style: textTheme.headlineSmall?.copyWith(color: AppColors.primary), - ), - ), + Widget _buildSurahListLoading() { + return ListView.separated( + itemCount: 10, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + separatorBuilder: (BuildContext context, int index) { + return Container( + height: 4.0, + ); + }, + itemBuilder: (BuildContext context, int index) { + return const BoxPlaceholder(width: double.infinity, height: 64.0); + }, + ); + } + + Widget _buildSurahListError(String? errorMessage) { + return Center( + child: IconButton( + onPressed: () async => await controller.getAllSurah(), + tooltip: AppString.refresh, + icon: const Icon( + Icons.replay, + size: 24.0, + ), + ), + ); + } + + Widget _buildSurahListSuccess(List? listSurah) { + return ListView.builder( + itemCount: listSurah?.length, + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + final surah = listSurah![index]; + return InkWell( + onTap: () { + controller.setLastRead(surah.nameTransliterationId); + Get.toNamed('${Routes.SURAH}/${surah.number}'); + }, + child: Card( + child: ListTile( + leading: CircleAvatar( + backgroundColor: Colors.transparent, + backgroundImage: const AssetImage(AppAssets.imgBorderSurah), + child: Text( + '${surah.number}', + style: Get.textTheme.titleLarge, ), - ); - }, - ); - } else { - return const Center( - child: Text( - 'Empty', - style: TextStyle( - fontSize: 16.0, + ), + title: Text(surah.nameTransliterationId, + style: Get.textTheme.titleLarge), + subtitle: + Text('${surah.revelationId} - ${surah.numberOfVerses} Ayat'), + trailing: Text( + surah.nameShort, + style: Get.textTheme.headlineSmall + ?.copyWith(color: AppColors.primary), ), ), - ); - } - }), + ), + ); + }, ); } - Widget _buildProgresJuz() { + Widget _buildSurahList() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Card( - child: Container( - width: Get.width, - padding: const EdgeInsets.all(24.0), - child: const Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '89%', - style: TextStyle( - fontSize: 20.0, - fontWeight: FontWeight.bold, - ), - ), - SizedBox( - height: 6.0, - ), - Text( - 'Progress Kamu', - style: TextStyle( - fontSize: 12.0, - ), - ), - SizedBox( - height: 4.0, - ), - LinearProgressIndicator( - value: 0.89, - // color: textColor1, - ), - SizedBox( - height: 4.0, - ), - Row( - children: [ - Text( - '1 Juz', - style: TextStyle( - fontSize: 10.0, - ), - ), - Spacer(), - Text( - '30 Juz', - style: TextStyle( - fontSize: 10.0, - ), - ), - ], - ), - ], - ), - ), - ), + child: controller.obx(_buildSurahListSuccess, + onLoading: _buildSurahListLoading(), + onError: _buildSurahListError, + onEmpty: const SizedBox.shrink()), ); } + // Widget _buildProgresJuz() { + // return Padding( + // padding: const EdgeInsets.symmetric(horizontal: 8.0), + // child: Card( + // child: Container( + // width: Get.width, + // padding: const EdgeInsets.all(24.0), + // child: const Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // '89%', + // style: TextStyle( + // fontSize: 20.0, + // fontWeight: FontWeight.bold, + // ), + // ), + // SizedBox( + // height: 6.0, + // ), + // Text( + // 'Progress Kamu', + // style: TextStyle( + // fontSize: 12.0, + // ), + // ), + // SizedBox( + // height: 4.0, + // ), + // LinearProgressIndicator( + // value: 0.89, + // // color: textColor1, + // ), + // SizedBox( + // height: 4.0, + // ), + // Row( + // children: [ + // Text( + // '1 Juz', + // style: TextStyle( + // fontSize: 10.0, + // ), + // ), + // Spacer(), + // Text( + // '30 Juz', + // style: TextStyle( + // fontSize: 10.0, + // ), + // ), + // ], + // ), + // ], + // ), + // ), + // ), + // ); + // } + Widget _buildHomeBanner() { return Padding( padding: const EdgeInsets.all(8.0), @@ -243,26 +264,26 @@ class HomeView extends GetView { fit: BoxFit.fill, ), ), - const Positioned.fill( + Positioned.fill( child: Padding( // color: Colors.black, - padding: EdgeInsets.all(20.0), + padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Icon( + const Icon( Icons.chrome_reader_mode_rounded, color: Colors.white, size: 24.0, ), - SizedBox( + const SizedBox( width: 8.0, ), Text( - 'Last Read', - style: TextStyle( + 'last_read'.tr, + style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w500, fontSize: 14.0, @@ -270,26 +291,26 @@ class HomeView extends GetView { ), ], ), - Spacer(), - Text( - 'Al-Fatihah', - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w600, - color: Colors.white, - ), - ), - SizedBox( + const Spacer(), + Obx(() => Text( + controller.lastRead, + style: TextStyle( + fontSize: 16.0, + fontWeight: bold, + color: Colors.white, + ), + )), + const SizedBox( height: 8.0, ), - Text( - 'Ayat No: 1', - style: TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w400, - color: Colors.white, - ), - ), + // const Text( + // 'Ayat No: 1', + // style: TextStyle( + // fontSize: 14.0, + // fontWeight: FontWeight.w400, + // color: Colors.white, + // ), + // ), ], ), ), diff --git a/lib/app/modules/lab_ui.dart b/lib/app/modules/lab_ui.dart index 4beaa8d..9f8f124 100644 --- a/lib/app/modules/lab_ui.dart +++ b/lib/app/modules/lab_ui.dart @@ -1,326 +1,226 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:suji/core/theme/colors.dart'; class Lab extends StatelessWidget { const Lab({super.key}); + // double heroItemHeight = 100; @override Widget build(BuildContext context) { return Scaffold( - resizeToAvoidBottomInset: false, - // backgroundColor: scaffoldColor, + extendBodyBehindAppBar: true, + appBar: AppBar( + title: const Text( + 'Tasbih Digital', + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + leading: const Icon( + Icons.arrow_back_ios, + color: AppColors.background, + ), + ), body: SafeArea( child: SingleChildScrollView( child: Column( + mainAxisSize: MainAxisSize.min, children: [ Container( - width: Get.width, - decoration: const BoxDecoration( - color: Colors.red, - borderRadius: BorderRadius.all( - Radius.circular( - 8.0, - ), - ), + padding: const EdgeInsets.all(8.0), + // color: Colors.green, + child: Center( + child: Text('1', + style: Get.textTheme.displayLarge?.copyWith( + fontSize: 46.0, + )), ), - child: Stack(children: [ - SizedBox( - width: Get.width, - child: Image.asset('assets/images/salah_banner.png', - fit: BoxFit.cover), - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - decoration: const ShapeDecoration( - color: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(12.0), - ), - )), - child: IconButton( - onPressed: () {}, - icon: const Icon( - Icons.arrow_back, - size: 24.0, - // color: scaffoldColor, - )), - ), - const SizedBox( - height: 8.0, - ), - const Text( - 'Jadwal Sholat', - style: TextStyle( - fontSize: 16.0, - color: Colors.white, - fontWeight: FontWeight.bold), - ), - const Text( - 'Surabaya', - style: TextStyle( - fontSize: 16.0, - color: Colors.white, - fontWeight: FontWeight.bold), - ), - const SizedBox( - height: 72.0, - ), - const Center( - child: Text( - 'Shubuh 4:17 AM', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24.0, - color: Colors.white, - fontWeight: FontWeight.bold), + ), + SizedBox( + height: 275.0, + // color: Colors.blue, + child: Stack( + fit: StackFit.expand, + alignment: Alignment.center, + children: [ + Positioned( + left: 0, + right: 0, + child: ElevatedButton( + onPressed: () {}, + style: ButtonStyle( + shadowColor: + MaterialStateProperty.all(Colors.black), + shape: MaterialStateProperty.all( + const CircleBorder( + side: BorderSide( + color: AppColors.onPrimary, + width: 2.0)))), + child: const Icon( + Icons.keyboard_arrow_up, + size: 200.0, + color: AppColors.background, ), ), - const Text( - 'Yang membedakan antara orang beriman dengan tidak beriman adalah meninggalkan salat.', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12.0, - fontStyle: FontStyle.italic, - color: Colors.white, + ), + Positioned( + bottom: 0, + child: ElevatedButton( + onPressed: () {}, + style: ButtonStyle( + shape: MaterialStateProperty.all( + const CircleBorder( + side: BorderSide( + color: AppColors.background, + width: 2.0)))), + child: const Icon( + Icons.keyboard_arrow_down, + color: AppColors.background, + size: 25.0, ), ), - ], - ), - ), - Positioned.fill( - child: Align( - alignment: Alignment.bottomCenter, - child: Container( - width: Get.width * .8, - height: 64.0, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(100.0), + ), + Positioned( + top: 0, + child: ElevatedButton( + onPressed: () {}, + style: ButtonStyle( + shape: MaterialStateProperty.all( + const CircleBorder( + side: BorderSide( + color: AppColors.background, + width: 2.0)))), + child: const Icon( + Icons.restart_alt, + color: AppColors.background, + size: 25.0, ), - child: const Center( - child: Text( - '30 September 2023', - style: TextStyle( - fontSize: 24.0, fontWeight: FontWeight.bold), - ), - )), - ), - ), - Positioned( - top: 8.0, - right: 8.0, - child: IconButton( - onPressed: () {}, - icon: const Icon( - Icons.notifications, - size: 48.0, - color: Colors.white, + ), ), - ), - ) - ]), + ]), ), - const SizedBox( - height: 8.0, + Flexible( + fit: FlexFit.loose, + child: Container( + margin: const EdgeInsets.only( + top: 16.0, + bottom: 16.0, + ), + child: Text( + 'Tasbih digital untuk membantu anda berdzikir setiap hari', + textAlign: TextAlign.center, + style: Get.textTheme.titleMedium), + ), ), - Column( - children: [ - Container( - padding: const EdgeInsets.symmetric( - vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric( - horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: const Row( - children: [ - ImageIcon( - AssetImage( - 'assets/images/Shalat-Shubuh.png', - ), - size: 24.0, - color: Colors.white, - ), - Spacer(), - Text( - 'Shubuh', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - Spacer(), - Text( - '04:48', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - ], + Flexible( + fit: FlexFit.loose, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + // color: Colors.red, + margin: const EdgeInsets.only( + left: 16.0, + ), + child: Text('Target', + textAlign: TextAlign.start, + style: Get.textTheme.titleLarge), ), - ), - Container( - padding: const EdgeInsets.symmetric( - vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric( - horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: const Row( - children: [ - ImageIcon( - AssetImage( - 'assets/images/Shalat-Zhuhur.png', + Container( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 8), + // color: Colors.orange, + child: TextFormField( + initialValue: '11', + keyboardType: + const TextInputType.numberWithOptions(signed: true), + decoration: InputDecoration( + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12.0), + borderSide: const BorderSide( + color: Colors.black12, + ), ), - size: 24.0, - color: Colors.white, - ), - Spacer(), - Text( - 'Dzuhur', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - Spacer(), - Text( - '04:48', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), + // helperText: 'Masukkan targetmu', ), - ], + onChanged: (value) {}, + ), ), - ), - Container( - padding: const EdgeInsets.symmetric( - vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric( - horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: const Row( - children: [ - ImageIcon( - AssetImage( - 'assets/images/Shalat-Ashar.png', + ], + ), + ), + Flexible( + fit: FlexFit.loose, + child: Container( + // height: 100.0, + padding: const EdgeInsets.all(8.0), + // color: Colors.red, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.all(16.0), + margin: const EdgeInsets.symmetric(horizontal: 8.0), + decoration: BoxDecoration( + color: Colors.green, + borderRadius: BorderRadius.circular(12.0), ), - size: 24.0, - color: Colors.white, - ), - Spacer(), - Text( - 'Ashar', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - Spacer(), - Text( - '04:48', - style: TextStyle( - color: Colors.white, + child: const Text( + '11', + textAlign: TextAlign.center, + style: TextStyle( fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric( - vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric( - horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: const Row( - children: [ - ImageIcon( - AssetImage( - 'assets/images/Shalat-Maghrib.png', + ), ), - size: 24.0, - color: Colors.white, - ), - Spacer(), - Text( - 'Maghrib', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - Spacer(), - Text( - '04:48', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), ), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric( - vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric( - horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: const Row( - children: [ - ImageIcon( - AssetImage( - 'assets/images/Shalat-Isya.png', + ), + Expanded( + child: Container( + padding: const EdgeInsets.all(16.0), + margin: const EdgeInsets.symmetric(horizontal: 8.0), + decoration: BoxDecoration( + color: Colors.green, + borderRadius: BorderRadius.circular(12.0), ), - size: 24.0, - color: Colors.white, - ), - Spacer(), - Text( - 'Isya', - style: TextStyle( - color: Colors.white, + child: const Text( + '33', + textAlign: TextAlign.center, + style: TextStyle( fontSize: 16.0, - fontWeight: FontWeight.bold), + ), + ), ), - Spacer(), - Text( - '04:48', - style: TextStyle( - color: Colors.white, + ), + Expanded( + child: Container( + padding: const EdgeInsets.all(16.0), + margin: const EdgeInsets.symmetric(horizontal: 8.0), + decoration: BoxDecoration( + color: Colors.green, + borderRadius: BorderRadius.circular(12.0), + ), + child: const Text( + '99', + textAlign: TextAlign.center, + style: TextStyle( fontSize: 16.0, - fontWeight: FontWeight.bold), + ), + ), ), - ], - ), + ), + ], ), - ], + ), + ), + Flexible( + fit: FlexFit.loose, + child: Container( + height: 100.0, + color: Colors.green, + ), ), ], ), @@ -328,4 +228,131 @@ class Lab extends StatelessWidget { ), ); } + + // Stack _buildHeroItem() { + // return Stack( + // // fit: StackFit.expand, + // children: [ + // Container( + // height: Get.height * 0.2, + // decoration: const BoxDecoration( + // color: Colors.orange, + // ), + // ), + // Transform.translate( + // offset: const Offset(0.0, 100), + // child: Center( + // child: Container( + // width: Get.width * 0.8, + // padding: const EdgeInsets.all(8.0), + // decoration: const BoxDecoration( + // color: Colors.white, + // boxShadow: [ + // BoxShadow( + // color: Colors.black12, + // blurRadius: 16.0, + // offset: Offset(.0, 6.0)) + // ], + // borderRadius: BorderRadius.all( + // Radius.circular( + // 8.0, + // ), + // ), + // ), + // child: SingleChildScrollView( + // scrollDirection: Axis.horizontal, + // controller: ScrollController(), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Container( + // width: 100, + // color: Colors.green, + // child: const Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.photo_camera_outlined, + // size: 20.0, + // color: Colors.red, + // ), + // Text( + // 'Jadwal Shalat', + // maxLines: 2, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // Container( + // width: 100, + // color: Colors.green, + // child: const Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.photo_camera_outlined, + // size: 20.0, + // color: Colors.red, + // ), + // Text( + // 'Jadwal Shalat', + // maxLines: 2, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // Container( + // width: 100, + // color: Colors.green, + // child: const Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.photo_camera_outlined, + // size: 20.0, + // color: Colors.red, + // ), + // Text( + // 'Jadwal Shalat', + // maxLines: 2, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // Container( + // width: 100, + // color: Colors.green, + // child: const Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.photo_camera_outlined, + // size: 20.0, + // color: Colors.red, + // ), + // Text( + // 'Jadwal Shalat', + // maxLines: 2, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // ], + // ), + // ), + // ), + // ), + // ), + // ], + // ); + // } } diff --git a/lib/app/modules/menu/bindings/menu_binding.dart b/lib/app/modules/menu/bindings/menu_binding.dart new file mode 100644 index 0000000..87bef12 --- /dev/null +++ b/lib/app/modules/menu/bindings/menu_binding.dart @@ -0,0 +1,33 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:get/get.dart'; +import 'package:suji/app/data/datasources/database_helper.dart'; +import 'package:suji/app/data/datasources/surah_local_data_source.dart'; +import 'package:suji/app/data/datasources/surah_remote_data_source.dart'; +import 'package:suji/app/data/repository/surah_repository_impl.dart'; +import 'package:suji/app/data/services/location_service.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_daily_pray_usecase.dart'; +import 'package:suji/app/modules/menu/controllers/menu_controller.dart'; + +class MenuBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => MenuController( + getDailyPrayUsecase: Get.find(), + locationService: Get.find()), + ); + Get.lazyPut(() => + GetDailyPrayUsecase(surahRepository: Get.find())); + Get.lazyPut(() => SurahRepositoryImpl( + connectivity: Get.find(), + surahRemoteDataSource: Get.find(), + surahLocalDataSource: Get.find())); + Get.lazyPut(() => Connectivity(), fenix: true); + Get.lazyPut(() => SurahRemoteDataSourceImpl()); + Get.lazyPut(() => + SurahLocalDataSourceImpl(databaseHelper: Get.find())); + Get.lazyPut(() => DatabaseHelper(), fenix: true); + Get.lazyPut(() => LocationService()); + } +} diff --git a/lib/app/modules/menu/controllers/menu_controller.dart b/lib/app/modules/menu/controllers/menu_controller.dart new file mode 100644 index 0000000..2cd5d2d --- /dev/null +++ b/lib/app/modules/menu/controllers/menu_controller.dart @@ -0,0 +1,71 @@ +import 'package:flutter/widgets.dart'; +import 'package:get/get.dart'; +import 'package:suji/app/data/services/location_service.dart'; +import 'package:suji/app/data/services/notification_service.dart'; +import 'package:suji/app/domain/entities/daily_pray.dart'; +import 'package:suji/app/domain/usescases/get_daily_pray_usecase.dart'; +import 'package:suji/app/widgets/custom_snackbar.dart'; +import 'package:suji/core/theme/colors.dart'; + +class MenuController extends GetxController with StateMixin> { + final GetDailyPrayUsecase getDailyPrayUsecase; + final LocationService locationService; + + // final _state = BaseState.empty.obs; + // BaseState get state => _state.value; + + // final _message = ''.obs; + // String get message => _message.value; + + // final RxList _dataDailyPray = [].obs; + // List get dataDailyPray => _dataDailyPray; + + MenuController( + {required this.getDailyPrayUsecase, required this.locationService}); + + @override + void onInit() async { + super.onInit(); + await getDailyPray(); + NotificationService.configureSelectNotificationSubject(); + // port.listen((_) => BackgroundService.alarmFired()); + } + + @override + void onClose() { + selectNotificationSubject.close(); + super.onClose(); + } + + toShalat() async { + try { + await locationService.determinePosition(); + return Get.toNamed('/shalat'); + } catch (e) { + Get.snackbar( + 'information'.tr, + e.toString(), + snackPosition: SnackPosition.BOTTOM, + margin: const EdgeInsets.all(8.0), + backgroundColor: AppColors.error, + ); + } + } + + Future getDailyPray() async { + // _state.value = BaseState.loading; + change([], status: RxStatus.loading()); + final result = await getDailyPrayUsecase.invoke(); + + result.fold((failure) { + // _message.value = l.toString(); + // _state.value = BaseState.error; + showErrorMessage(failure.message); + change([], status: RxStatus.error(failure.message)); + }, (result) { + // _dataDailyPray.assignAll(r); + // _state.value = BaseState.success; + change(result, status: RxStatus.success()); + }); + } +} diff --git a/lib/app/modules/menu/views/menu_view.dart b/lib/app/modules/menu/views/menu_view.dart new file mode 100644 index 0000000..a36aa55 --- /dev/null +++ b/lib/app/modules/menu/views/menu_view.dart @@ -0,0 +1,429 @@ +import 'package:flutter/material.dart' hide MenuController; +import 'package:get/get.dart'; +import 'package:suji/app/domain/entities/daily_pray.dart'; +import 'package:suji/app/modules/menu/controllers/menu_controller.dart'; +import 'package:suji/app/routes/app_pages.dart'; +import 'package:suji/app/widgets/box_placeholder.dart'; +import 'package:suji/core/theme/colors.dart'; +import 'package:suji/core/theme/text_style.dart'; +import 'package:suji/core/values/constant.dart'; + +class MenuView extends GetView { + const MenuView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: SingleChildScrollView( + controller: ScrollController(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeroItem(), + Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 80.0, + ), + Text( + 'daily_pray'.tr, + style: const TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'daily_pray_reminder'.tr, + style: const TextStyle( + fontSize: 12.0, + ), + ), + controller.obx( + _buildSurahSuccess, + onLoading: const BoxPlaceholder(), + onError: _buildSurahError, + onEmpty: const SizedBox.shrink(), + ), + // Obx(() { + // if (controller.state == BaseState.loading) { + // return const BoxPlaceholder(); + // } else if (controller.state == BaseState.success) { + // return ListView.separated( + // itemCount: controller.dataDailyPray.length, + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // separatorBuilder: (BuildContext context, int index) { + // return const Divider( + // color: AppColors.primary, + // ); + // }, + // itemBuilder: (BuildContext context, int index) { + // final dataDailyPray = + // controller.dataDailyPray[index]; + // return GestureDetector( + // onTap: () => _showDaillyPrayDetail(dataDailyPray), + // child: ListTile( + // leading: const Icon(Icons.mosque), + // minLeadingWidth: 0.0, + // title: Text(dataDailyPray.title), + // trailing: const Icon( + // Icons.chevron_right, + // size: 24.0, + // ), + // ), + // ); + // }, + // ); + // } else if (controller.state == BaseState.error) { + // Get.snackbar( + // 'information'.tr, + // controller.message, + // snackPosition: SnackPosition.BOTTOM, + // margin: const EdgeInsets.all(8.0), + // backgroundColor: AppColors.error, + // ); + // return Center( + // child: IconButton( + // onPressed: () async => + // await controller.getDailyPray(), + // icon: const Icon( + // Icons.replay, + // size: 24.0, + // ), + // ), + // ); + // } else { + // return const SizedBox.shrink(); + // } + // }), + ], + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildSurahError(errorMessage) { + return Center( + child: IconButton( + onPressed: () async => await controller.getDailyPray(), + icon: const Icon( + Icons.replay, + size: 24.0, + ), + ), + ); + } + + Widget _buildSurahSuccess(state) { + return ListView.separated( + itemCount: state?.length ?? 0, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + separatorBuilder: (BuildContext context, int index) { + return const Divider( + color: AppColors.primary, + ); + }, + itemBuilder: (BuildContext context, int index) { + final dataDailyPray = state![index]; + return GestureDetector( + onTap: () => _showDaillyPrayDetail(dataDailyPray), + child: ListTile( + leading: const Icon(Icons.mosque), + minLeadingWidth: 0.0, + title: Text(dataDailyPray.title), + trailing: const Icon( + Icons.chevron_right, + size: 24.0, + ), + ), + ); + }, + ); + } + + Widget _buildHeroItem() { + return Stack( + children: [ + Container( + height: Get.height * 0.25, + width: double.infinity, + foregroundDecoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + AppColors.darkSurface.withOpacity(0.1), + AppColors.darkSurface, + ])), + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + AppAssets.imgBannerShalat, + ), + fit: BoxFit.cover, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('greeting'.tr, + style: Get.textTheme.headlineMedium?.copyWith( + fontWeight: medium, color: AppColors.onDarkSurface)), + Text( + 'user'.tr, + style: Get.textTheme.displayMedium + ?.copyWith(color: AppColors.onDarkSurface), + ), + ], + ), + ), + Transform.translate( + offset: const Offset(0.0, 110), + child: Center( + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(8.0), + margin: const EdgeInsets.all(16.0), + decoration: const BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 16.0, + offset: Offset(.0, 6.0)) + ], + borderRadius: BorderRadius.all( + Radius.circular( + 8.0, + ), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: controller.toShalat, + child: Container( + width: 80, + height: 80, + color: Colors.transparent, + margin: const EdgeInsets.only(top: 8.0), + alignment: Alignment.topCenter, + child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(4.0), + decoration: BoxDecoration( + border: Border.all(color: AppColors.primary), + borderRadius: BorderRadius.circular(12)), + child: const ImageIcon( + AssetImage( + AppAssets.iconPrayerTime, + ), + size: 32.0, + color: AppColors.primary), + ), + Text( + 'prayer_time'.tr, + maxLines: 2, + style: Get.textTheme.bodySmall, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + GestureDetector( + onTap: () => Get.toNamed(Routes.HOME), + child: Container( + width: 80, + height: 80, + color: Colors.transparent, + margin: const EdgeInsets.only(top: 8.0), + alignment: Alignment.topCenter, + child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(4.0), + decoration: BoxDecoration( + border: Border.all(color: AppColors.primary), + borderRadius: BorderRadius.circular(12)), + child: const ImageIcon( + AssetImage( + AppAssets.iconQuran, + ), + size: 32.0, + color: AppColors.primary), + ), + Text( + AppString.alQuran, + maxLines: 2, + style: Get.textTheme.bodySmall, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + GestureDetector( + onTap: () => Get.toNamed(AppPages.tasbih), + child: Container( + width: 80, + height: 80, + color: Colors.transparent, + margin: const EdgeInsets.only(top: 8.0), + alignment: Alignment.topCenter, + child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(4.0), + decoration: BoxDecoration( + border: Border.all(color: AppColors.primary), + borderRadius: BorderRadius.circular(12)), + child: const ImageIcon( + AssetImage( + AppAssets.iconTasbih, + ), + size: 32.0, + color: AppColors.primary), + ), + Text( + AppString.tasbihDigital, + maxLines: 2, + style: Get.textTheme.bodySmall, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + GestureDetector( + onTap: () => Get.toNamed(AppPages.asmaulHusna), + child: Container( + width: 80, + height: 80, + color: Colors.transparent, + margin: const EdgeInsets.only(top: 8.0), + alignment: Alignment.topCenter, + child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(4.0), + decoration: BoxDecoration( + border: Border.all(color: AppColors.primary), + borderRadius: BorderRadius.circular(12)), + child: const ImageIcon( + AssetImage( + AppAssets.iconShalat, + ), + size: 32.0, + color: AppColors.primary), + ), + Text( + AppString.asmaulHusna, + maxLines: 2, + style: Get.textTheme.bodySmall, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ], + ); + } + + void _showDaillyPrayDetail(DailyPray dailyPray) { + Get.bottomSheet( + SingleChildScrollView( + child: Container( + padding: const EdgeInsets.all(24), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + dailyPray.title, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + Text( + dailyPray.arabic, + style: Get.textTheme.titleLarge?.copyWith( + fontSize: 16.0, + ), + textAlign: TextAlign.right, + ), + Text( + dailyPray.latin, + style: Get.textTheme.bodyLarge + ?.copyWith(color: AppColors.primary), + textAlign: TextAlign.justify, + ), + Text( + dailyPray.translation, + textAlign: TextAlign.justify, + ), + const SizedBox( + height: 8.0, + ), + SizedBox( + height: Get.height * 0.07, + width: Get.width, + child: ElevatedButton( + style: ButtonStyle( + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ))), + onPressed: () => Get.back(), + child: Text( + 'back'.tr, + style: const TextStyle( + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ), + ), + isScrollControlled: true, + ); + } +} diff --git a/lib/app/modules/search/bindings/search_binding.dart b/lib/app/modules/search/bindings/search_binding.dart index 107e4d3..90ae417 100644 --- a/lib/app/modules/search/bindings/search_binding.dart +++ b/lib/app/modules/search/bindings/search_binding.dart @@ -1,9 +1,18 @@ import 'package:get/get.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_all_surah_usecase.dart'; +import 'package:suji/app/domain/usescases/get_surah_by_query_usecase.dart'; import 'package:suji/app/modules/search/controllers/search_controller.dart'; class SearchBinding implements Bindings { @override void dependencies() { - Get.lazyPut(() => MySearchController()); + Get.lazyPut(() => MySearchController( + getAllSurahUsecase: Get.find(), + getSurahByQueryUsecase: Get.find())); + Get.lazyPut( + () => GetAllSurahUsecase(surahRepository: Get.find())); + Get.lazyPut(() => + GetSurahByQueryUsecase(surahRepository: Get.find())); } } diff --git a/lib/app/modules/search/controllers/search_controller.dart b/lib/app/modules/search/controllers/search_controller.dart index 5896fca..694f9ef 100644 --- a/lib/app/modules/search/controllers/search_controller.dart +++ b/lib/app/modules/search/controllers/search_controller.dart @@ -1,27 +1,36 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; import 'package:suji/app/domain/entities/surah.dart'; -import 'package:suji/app/domain/repository/surah_repository.dart'; -import 'package:suji/core/theme/colors.dart'; -import 'package:suji/core/utils/base_state.dart'; +import 'package:suji/app/domain/usescases/get_all_surah_usecase.dart'; +import 'package:suji/app/domain/usescases/get_surah_by_query_usecase.dart'; +import 'package:suji/app/widgets/custom_snackbar.dart'; -class MySearchController extends GetxController { - final SurahRepository surahRepository = Get.find(); +class MySearchController extends GetxController with StateMixin> { + final GetSurahByQueryUsecase getSurahByQueryUsecase; + final GetAllSurahUsecase getAllSurahUsecase; + Box box = Hive.box('sujiSettingsBox'); + + MySearchController( + {required this.getSurahByQueryUsecase, required this.getAllSurahUsecase}); final TextEditingController searchBarController = TextEditingController(); final _isTyping = false.obs; bool get isTyping => _isTyping.value; - final _state = BaseState.empty.obs; - BaseState get state => _state.value; + // final _state = BaseState.empty.obs; + // BaseState get state => _state.value; + + // final _message = ''.obs; + // String get message => _message.value; - final RxList _listSurah = [].obs; - List get listSurah => _listSurah; + // final RxList _listSurah = [].obs; + // List get listSurah => _listSurah; @override - void onInit() { + void onInit() async { super.onInit(); - getAllSurah(); + await getAllSurah(); } @override @@ -30,6 +39,10 @@ class MySearchController extends GetxController { super.dispose(); } + void setLastRead(String surahName) { + box.put('lastRead', surahName); + } + void isTypingCheck() { _isTyping.value = searchBarController.text.isNotEmpty; } @@ -41,36 +54,37 @@ class MySearchController extends GetxController { } Future getAllSurah() async { - _state.value = BaseState.loading; - final result = await surahRepository.getAllSurah(); + // _state.value = BaseState.loading; + change([], status: RxStatus.loading()); + final result = await getAllSurahUsecase.invoke(); result.fold((failure) { - _state.value = BaseState.error; - Get.snackbar('Information', failure.message, - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error); + // _message.value = failure.message; + // _state.value = BaseState.error; + showErrorMessage(failure.message); + change([], status: RxStatus.error(failure.message)); }, (surahData) { - _listSurah.assignAll(surahData); - _state.value = BaseState.success; + // _listSurah.assignAll(surahData); + // _state.value = BaseState.success; + change(surahData, status: RxStatus.success()); }); } Future getSurahByQuery() async { - _state.value = BaseState.loading; + // _state.value = BaseState.loading; + change([], status: RxStatus.loading()); final result = - await surahRepository.getSurahByQuery(searchBarController.text); + await getSurahByQueryUsecase.invoke(searchBarController.text); result.fold((failure) { - _state.value = BaseState.error; - Get.snackbar('Information', failure.message, - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error - ); + // _message.value = failure.message; + // _state.value = BaseState.error; + showErrorMessage(failure.message); + change([], status: RxStatus.error(failure.message)); }, (surahData) { - _listSurah.assignAll(surahData); - _state.value = BaseState.success; + // _listSurah.assignAll(surahData); + // _state.value = BaseState.success; + change(surahData, status: RxStatus.success()); }); } } diff --git a/lib/app/modules/search/views/search_view.dart b/lib/app/modules/search/views/search_view.dart index 8580afa..ab4dbf9 100644 --- a/lib/app/modules/search/views/search_view.dart +++ b/lib/app/modules/search/views/search_view.dart @@ -3,7 +3,6 @@ import 'package:get/get.dart'; import 'package:suji/app/routes/app_pages.dart'; import 'package:suji/app/widgets/box_placeholder.dart'; import 'package:suji/core/theme/colors.dart'; -import 'package:suji/core/utils/base_state.dart'; import '../controllers/search_controller.dart'; class SearchView extends GetView { @@ -27,80 +26,167 @@ class SearchView extends GetView { } Widget _buildSearchResult() { - return Obx(() { - if (controller.state == BaseState.loading) { - return Expanded( - child: ListView.separated( - shrinkWrap: true, - itemCount: 10, - physics: const BouncingScrollPhysics(), - separatorBuilder: (BuildContext context, int index) { - return Container( - height: 4.0, - ); - }, - itemBuilder: (BuildContext context, int index) { - return const BoxPlaceholder(width: double.infinity, height: 64.0); + return controller.obx( + _buildSearchResultSuccess, + onLoading: _buildSearchResultLoading(), + onError: _buildSearchResultError, + onEmpty: const SizedBox.shrink(), + ); + // return Obx(() { + // if (controller.state == BaseState.loading) { + // return Expanded( + // child: ListView.separated( + // shrinkWrap: true, + // itemCount: 10, + // physics: const BouncingScrollPhysics(), + // separatorBuilder: (BuildContext context, int index) { + // return Container( + // height: 4.0, + // ); + // }, + // itemBuilder: (BuildContext context, int index) { + // return const BoxPlaceholder(width: double.infinity, height: 64.0); + // }, + // ), + // ); + // } else if (controller.state == BaseState.error) { + // Get.snackbar('information'.tr, controller.message, + // snackPosition: SnackPosition.BOTTOM, + // margin: const EdgeInsets.all(8.0), + // backgroundColor: AppColors.error); + // return Center( + // child: IconButton( + // onPressed: () async => await controller.getAllSurah(), + // icon: const Icon( + // Icons.replay, + // size: 24, + // ), + // ), + // ); + // } else if (controller.state == BaseState.success) { + // return Expanded( + // child: ListView.builder( + // itemCount: controller.listSurah.length, + // physics: const BouncingScrollPhysics(), + // shrinkWrap: true, + // itemBuilder: (BuildContext context, int index) { + // final textTheme = Theme.of(context).textTheme; + // final surah = controller.listSurah[index]; + // return InkWell( + // onTap: () => Get.toNamed('${Routes.SURAH}/${surah.number}'), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 8.0), + // child: Card( + // child: ListTile( + // leading: CircleAvatar( + // backgroundColor: Colors.transparent, + // backgroundImage: + // const AssetImage('assets/images/border_surah.png'), + // child: Text( + // '${surah.number}', + // style: textTheme.titleLarge, + // ), + // ), + // title: Text(surah.nameTransliterationId, style: textTheme.titleLarge,), + // subtitle: Text('${surah.numberOfVerses} Ayat'), + // trailing: Text( + // surah.nameShort, + // style: textTheme.headlineSmall?.copyWith(color: AppColors.primary), + // ), + // ), + // ), + // ), + // ); + // }, + // ), + // ); + // } else { + // return const Center( + // child: Text( + // 'Empty', + // style: TextStyle( + // fontSize: 16.0, + // ), + // ), + // ); + // } + // }); + } + + Widget _buildSearchResultError(errorMessage) { + return Center( + child: IconButton( + onPressed: () async => await controller.getAllSurah(), + icon: const Icon( + Icons.replay, + size: 24, + ), + ), + ); + } + + Expanded _buildSearchResultLoading() { + return Expanded( + child: ListView.separated( + shrinkWrap: true, + itemCount: 10, + physics: const BouncingScrollPhysics(), + separatorBuilder: (BuildContext context, int index) { + return Container( + height: 4.0, + ); + }, + itemBuilder: (BuildContext context, int index) { + return const BoxPlaceholder(width: double.infinity, height: 64.0); + }, + ), + ); + } + + Widget _buildSearchResultSuccess(state) { + return Expanded( + child: ListView.builder( + itemCount: state?.length, + physics: const BouncingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (BuildContext context, int index) { + final textTheme = Theme.of(context).textTheme; + final surah = state![index]; + return InkWell( + onTap: () { + controller.setLastRead(surah.nameTransliterationId); + Get.toNamed('${Routes.SURAH}/${surah.number}'); }, - ), - ); - } else if (controller.state == BaseState.error) { - return const Center( - child: Text( - 'Something Wrong :(', - style: TextStyle( - fontSize: 16.0, - ), - ), - ); - } else if (controller.state == BaseState.success) { - return Expanded( - child: ListView.builder( - itemCount: controller.listSurah.length, - physics: const BouncingScrollPhysics(), - shrinkWrap: true, - itemBuilder: (BuildContext context, int index) { - final textTheme = Theme.of(context).textTheme; - final surah = controller.listSurah[index]; - return InkWell( - onTap: () => Get.toNamed('${Routes.SURAH}/${surah.number}'), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Card( - child: ListTile( - leading: CircleAvatar( - backgroundColor: Colors.transparent, - backgroundImage: - const AssetImage('assets/images/border_surah.png'), - child: Text( - '${surah.number}', - style: textTheme.titleLarge, - ), - ), - title: Text(surah.nameTransliterationId, style: textTheme.titleLarge,), - subtitle: Text('${surah.numberOfVerses} Ayat'), - trailing: Text( - surah.nameShort, - style: textTheme.headlineSmall?.copyWith(color: AppColors.primary), - ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Card( + child: ListTile( + leading: CircleAvatar( + backgroundColor: Colors.transparent, + backgroundImage: + const AssetImage('assets/images/border_surah.png'), + child: Text( + '${surah.number}', + style: textTheme.titleLarge, ), ), + title: Text( + surah.nameTransliterationId, + style: textTheme.titleLarge, + ), + subtitle: Text('${surah.numberOfVerses} Ayat'), + trailing: Text( + surah.nameShort, + style: textTheme.headlineSmall + ?.copyWith(color: AppColors.primary), + ), ), - ); - }, - ), - ); - } else { - return const Center( - child: Text( - 'Empty', - style: TextStyle( - fontSize: 16.0, + ), ), - ), - ); - } - }); + ); + }, + ), + ); } Widget _buildSearchBar() { diff --git a/lib/app/modules/shalat/bindings/shalat_binding.dart b/lib/app/modules/shalat/bindings/shalat_binding.dart index 41523c5..1131833 100644 --- a/lib/app/modules/shalat/bindings/shalat_binding.dart +++ b/lib/app/modules/shalat/bindings/shalat_binding.dart @@ -1,14 +1,33 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:get/get.dart'; +import 'package:suji/app/data/datasources/database_helper.dart'; +import 'package:suji/app/data/datasources/shalat_local_data_source.dart'; +import 'package:suji/app/data/datasources/shalat_remote_data_source.dart'; import 'package:suji/app/data/repository/shalat_repository_impl.dart'; +import 'package:suji/app/data/services/location_service.dart'; import 'package:suji/app/domain/repository/shalat_repository.dart'; +import 'package:suji/app/domain/usescases/get_shalat_time_usecase.dart'; import 'package:suji/app/modules/shalat/controllers/notification_controller.dart'; import 'package:suji/app/modules/shalat/controllers/shalat_controller.dart'; class ShalatBinding implements Bindings { @override void dependencies() { - Get.lazyPut(() => ShalatController()); - Get.lazyPut(() => ShalatRepositoryImpl()); Get.lazyPut(() => NotificationController()); + Get.lazyPut(() => ShalatController( + locationService: Get.find(), + getShalatTimeUsecase: Get.find())); + Get.lazyPut(() => + GetShalatTimeUsecase(shalatRepository: Get.find())); + Get.lazyPut(() => ShalatRepositoryImpl( + connectivity: Get.find(), + shalatLocalDataSource: Get.find(), + shalatRemoteDataSource: Get.find())); + Get.lazyPut(() => Connectivity(), fenix: true); + Get.lazyPut( + () => ShalatLocalDataSourceImpl( + databaseHelper: Get.find()), + fenix: true); + Get.lazyPut(() => ShalatRemoteDataSourceImpl()); } } diff --git a/lib/app/modules/shalat/controllers/notification_controller.dart b/lib/app/modules/shalat/controllers/notification_controller.dart index f34f4a1..623beba 100644 --- a/lib/app/modules/shalat/controllers/notification_controller.dart +++ b/lib/app/modules/shalat/controllers/notification_controller.dart @@ -1,18 +1,16 @@ import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart'; -import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; -import 'package:intl/intl.dart'; import 'package:suji/app/data/services/background_service.dart'; import 'package:suji/app/domain/entities/shalat.dart'; import 'package:suji/app/domain/repository/shalat_repository.dart'; -import 'package:suji/core/theme/colors.dart'; +import 'package:suji/app/widgets/custom_snackbar.dart'; import 'package:suji/core/utils/date_utils.dart'; class NotificationController extends GetxController { final ShalatRepository shalatRepository = Get.find(); - Box box = Hive.box('alarmSettings'); + Box box = Hive.box('sujiSettingsBox'); final _isAlarmActive = false.obs; bool get isAlarmActive => _isAlarmActive.value; @@ -28,50 +26,20 @@ class NotificationController extends GetxController { box.put('isAlarmActive', isAlarmActive); isAlarmActive ? _startAlarm(shalatTime) : _stopAlarm(); } else { - Get.snackbar('Information', 'Failed alarm setup', - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error); + showErrorMessage('failed alarm setup'.tr); } } _startAlarm(Shalat shalatTime) async { - // print('lah'); - // print(format().toString()); + // await NotificationService().showNotification(flutterLocalNotificationsPlugin); + showSuccessMessage('alarm setup actived'.tr); return await AndroidAlarmManager.oneShotAt( - // const Duration(minutes: 1), - DateTimeUtils.getPrayerTime(shalatTime), - 0, - // () async => await BackgroundService.callback(), - // BackgroundService.printHello, - BackgroundService.callback, - // startAt: format(), - exact: true, - wakeup: true); + DateTimeUtils.getPrayerTime(shalatTime), 0, BackgroundService.callback, + exact: true, rescheduleOnReboot: true, wakeup: true); } _stopAlarm() async { await AndroidAlarmManager.cancel(0); - } - - static DateTime format() { - // Date and Time Format - final now = DateTime.now(); - final dateFormat = DateFormat('y/M/d'); - const timeSpecific = '01:59:00'; - final completeFormat = DateFormat('y/M/d H:m:s'); - - // Today Format - final todayDate = dateFormat.format(now); - final todayDateAndTime = '$todayDate $timeSpecific'; - var resultToday = completeFormat.parseStrict(todayDateAndTime); - - // Tomorrow Format - var formatted = resultToday.add(const Duration(days: 1)); - final tomorrowDate = dateFormat.format(formatted); - final tomorrowDateAndTime = '$tomorrowDate $timeSpecific'; - var resultTomorrow = completeFormat.parseStrict(tomorrowDateAndTime); - - return now.isAfter(resultToday) ? resultTomorrow : resultToday; + showSuccessMessage('alarm setup deactived'.tr); } } diff --git a/lib/app/modules/shalat/controllers/shalat_controller.dart b/lib/app/modules/shalat/controllers/shalat_controller.dart index bcad4fc..4e27c47 100644 --- a/lib/app/modules/shalat/controllers/shalat_controller.dart +++ b/lib/app/modules/shalat/controllers/shalat_controller.dart @@ -1,74 +1,78 @@ -import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; import 'package:suji/app/data/services/location_service.dart'; import 'package:suji/app/domain/entities/shalat.dart'; -import 'package:suji/app/domain/repository/shalat_repository.dart'; -import 'package:suji/core/theme/colors.dart'; -import 'package:suji/core/utils/base_state.dart'; +import 'package:suji/app/domain/usescases/get_shalat_time_usecase.dart'; +import 'package:suji/app/widgets/custom_snackbar.dart'; import 'package:suji/core/utils/logger.dart'; -class ShalatController extends GetxController { - final ShalatRepository shalatRepository = Get.find(); - final LocationService _locationService = LocationService(); +class ShalatController extends GetxController with StateMixin { + final GetShalatTimeUsecase getShalatTimeUsecase; + final LocationService locationService; - final _state = BaseState.empty.obs; - BaseState get state => _state.value; + ShalatController( + {required this.getShalatTimeUsecase, required this.locationService}); - final _shalat = Shalat().obs; - Shalat get shalat => _shalat.value; + // final _state = BaseState.empty.obs; + // BaseState get state => _state.value; + + // final _message = ''.obs; + // String get message => _message.value; + + // final _shalat = Shalat().obs; + // Shalat get shalat => _shalat.value; + + DateTime date = DateTime.now(); + Position? position; + + nextDay() async { + if (position != null) { + date = date.add(const Duration(days: 1)); + await getShalatTime(position!); + } + } + + beforeDay() async { + if (position != null) { + date = date.subtract(const Duration(days: 1)); + await getShalatTime(position!); + } + } @override void onInit() async { super.onInit(); - try { - final position = await _locationService.determinePosition(); - getShalatTime(position); - } catch (e) { - Get.back(); - Get.snackbar( - 'Information', - e.toString(), - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error, - ); + position = await Geolocator.getLastKnownPosition(); + if (position != null) { + await getShalatTime(position!); } } Future refreshLocation() async { try { - final position = await _locationService.determinePosition(); - shalatRepository.fetchShalatTime(position); - getShalatTime(position); + position = await locationService.determinePosition(); + if (position != null) { + await getShalatTime(position!); + } } catch (e) { - Get.back(); - Get.snackbar( - 'Information', - e.toString(), - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error, - ); + showErrorMessage(e.toString()); } } - void getShalatTime(Position position) async { - _state.value = BaseState.loading; - final dataShalatTime = await shalatRepository.getShalatTime(position); + Future getShalatTime(Position position) async { + // _state.value = BaseState.loading; + change(null, status: RxStatus.loading()); + final dataShalatTime = await getShalatTimeUsecase.invoke(date, position); dataShalatTime.fold((failure) { - _state.value = BaseState.error; - Get.snackbar( - 'Information', - failure.message, - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error, - ); + // _message.value = failure.message; + // _state.value = BaseState.error; Log.e('[ShalatController][dataShalatTime]', failure.message); + showErrorMessage(failure.message); + change(null, status: RxStatus.error(failure.message)); }, (result) { - _shalat(result); - _state.value = BaseState.success; + // _shalat(result); + // _state.value = BaseState.success; + change(result, status: RxStatus.success()); }); } } diff --git a/lib/app/modules/shalat/views/shalat_view.dart b/lib/app/modules/shalat/views/shalat_view.dart index 5c8abf7..1c4dc8c 100644 --- a/lib/app/modules/shalat/views/shalat_view.dart +++ b/lib/app/modules/shalat/views/shalat_view.dart @@ -1,8 +1,9 @@ -import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:suji/app/modules/shalat/controllers/notification_controller.dart'; import 'package:suji/app/modules/shalat/controllers/shalat_controller.dart'; +import 'package:suji/app/widgets/box_placeholder.dart'; import 'package:suji/core/theme/colors.dart'; import 'package:suji/core/values/constant.dart'; @@ -32,6 +33,7 @@ class ShalatView extends GetView { } Widget _buildHeroShalat() { + const double heightOfDateContainer = 48.0; return Container( width: Get.width, decoration: const BoxDecoration( @@ -43,7 +45,7 @@ class ShalatView extends GetView { ), ), child: Stack(children: [ - Container( + SizedBox( width: Get.width, child: Image.asset(AppAssets.imgBannerShalat, fit: BoxFit.cover), ), @@ -63,34 +65,46 @@ class ShalatView extends GetView { child: IconButton( onPressed: () => Get.back(), icon: const Icon( - Icons.arrow_back, - size: 24.0, - // color: scaffoldColor, + Icons.arrow_back_ios_new, )), ), const SizedBox( height: 8.0, ), Text( - 'prayer time'.tr, - style: TextStyle( + 'prayer_time'.tr, + style: const TextStyle( fontSize: 16.0, color: Colors.white, fontWeight: FontWeight.bold), ), Row( children: [ - Obx(() => Text( - '${controller.shalat.metaTimezone}', - style: const TextStyle( - fontSize: 16.0, - color: Colors.white, - fontWeight: FontWeight.bold), - )), + controller.obx( + (dataShalat) => Text( + dataShalat?.metaTimezone ?? '-', + style: const TextStyle( + fontSize: 16.0, + color: Colors.white, + fontWeight: FontWeight.bold), + ), + onError: (_) => const Text( + '-', + style: TextStyle( + fontSize: 16.0, + color: Colors.white, + fontWeight: FontWeight.bold), + ), + ), + // Obx(() => Text( + // controller.shalat.metaTimezone ?? '-', + // style: const TextStyle( + // fontSize: 16.0, + // color: Colors.white, + // fontWeight: FontWeight.bold), + // )), IconButton( - onPressed: () async { - await controller.refreshLocation(); - }, + onPressed: () async => await controller.refreshLocation(), icon: const Icon( Icons.replay, size: 24.0, @@ -117,7 +131,7 @@ class ShalatView extends GetView { }), ), Text( - 'shalat motivation'.tr, + 'shalat_motivation'.tr, textAlign: TextAlign.center, style: const TextStyle( fontSize: 12.0, @@ -131,20 +145,48 @@ class ShalatView extends GetView { Positioned.fill( child: Align( alignment: Alignment.bottomCenter, - child: Container( - width: Get.width * .8, - height: 64.0, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(100.0), - ), - child: Center( - child: Obx(() => Text( - '${controller.shalat.dateReadable}', - style: const TextStyle( - fontSize: 24.0, fontWeight: FontWeight.bold), - )), - )), + child: Transform.translate( + offset: const Offset(0.0, heightOfDateContainer / 2), + child: Container( + width: Get.width * .6, + height: heightOfDateContainer, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(100.0), + boxShadow: const [ + BoxShadow( + color: AppColors.primary, + blurRadius: 16.0, + offset: Offset(0.0, 6.0)), + ]), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + onPressed: () async => controller.beforeDay(), + icon: const Icon( + Icons.arrow_back_ios_new, + size: 16.0, + ), + ), + controller.obx((state) { + return Text( + state?.dateReadable ?? '-', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 24.0, fontWeight: FontWeight.bold), + ); + }, onError: (_) => const SizedBox.shrink()), + IconButton( + onPressed: () async => controller.nextDay(), + icon: const Icon( + Icons.arrow_forward_ios, + size: 16.0, + ), + ), + ])), + ), ), ), Positioned( @@ -153,12 +195,10 @@ class ShalatView extends GetView { child: Obx(() { if (notificationController.isAlarmActive) { return IconButton( - onPressed: () async { - notificationController.setAlarm(controller.shalat); - // final notificationService = NotificationService(); - // await notificationService.showNotification(flutterLocalNotificationsPlugin); - }, - tooltip: 'Alarm', + onPressed: () => controller.status.isSuccess + ? notificationController.setAlarm(controller.state!) + : null, + tooltip: AppString.alarm, icon: const Icon( Icons.notifications_active_outlined, size: 48.0, @@ -167,10 +207,10 @@ class ShalatView extends GetView { ); } else { return IconButton( - onPressed: () { - notificationController.setAlarm(controller.shalat); - }, - tooltip: 'Alarm', + onPressed: () => controller.status.isSuccess + ? notificationController.setAlarm(controller.state!) + : null, + tooltip: AppString.alarm, icon: const Icon( Icons.notifications_none_outlined, size: 48.0, @@ -185,189 +225,434 @@ class ShalatView extends GetView { } Widget _buildPrayerTime() { - return Column( - children: [ - Container( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: Row( - children: [ - const ImageIcon( - AssetImage( - AppAssets.iconShalatShubuh, - ), - size: 24.0, - color: Colors.white, - ), - const Spacer(), - const Text( - 'Shubuh', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - const Spacer(), - Obx(() => Text( - '${controller.shalat.timingsFajr}', - style: const TextStyle( + return Padding( + padding: const EdgeInsets.only(top: 16.0), + child: controller.obx( + (state) { + final dataShalat = state; + return Column( + children: [ + Container( + padding: const EdgeInsets.symmetric( + vertical: 16.0, horizontal: 32.0), + margin: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 2.0), + decoration: ShapeDecoration( + color: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + )), + child: Row( + children: [ + const ImageIcon( + AssetImage( + AppAssets.iconShalatShubuh, + ), + size: 24.0, color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - )), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: Row( - children: [ - const ImageIcon( - AssetImage( - AppAssets.iconShalatDzuhr, + ), + const SizedBox(width: 8), + const Text( + AppString.shubuh, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + const Spacer(), + Text( + '${dataShalat?.timingsFajr}', + style: const TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + ], + ), ), - size: 24.0, - color: Colors.white, - ), - const Spacer(), - const Text( - 'Dzuhur', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - const Spacer(), - Obx(() => Text( - '${controller.shalat.timingsDhuhr}', - style: const TextStyle( + Container( + padding: const EdgeInsets.symmetric( + vertical: 16.0, horizontal: 32.0), + margin: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 2.0), + decoration: ShapeDecoration( + color: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + )), + child: Row( + children: [ + const ImageIcon( + AssetImage( + AppAssets.iconShalatDzuhr, + ), + size: 24.0, color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - )), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: Row( - children: [ - const ImageIcon( - AssetImage( - AppAssets.iconShalatAshr, + ), + const SizedBox(width: 8), + const Text( + AppString.dzuhr, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + const Spacer(), + Text( + '${dataShalat?.timingsDhuhr}', + style: const TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + ], + ), ), - size: 24.0, - color: Colors.white, - ), - const Spacer(), - const Text( - 'Ashar', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - const Spacer(), - Obx(() => Text( - '${controller.shalat.timingsAsr}', - style: const TextStyle( + Container( + padding: const EdgeInsets.symmetric( + vertical: 16.0, horizontal: 32.0), + margin: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 2.0), + decoration: ShapeDecoration( + color: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + )), + child: Row( + children: [ + const ImageIcon( + AssetImage( + AppAssets.iconShalatAshr, + ), + size: 24.0, color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - )), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: Row( - children: [ - const ImageIcon( - AssetImage( - AppAssets.iconShalatMaghrib, + ), + const SizedBox(width: 8), + const Text( + AppString.ashr, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + const Spacer(), + Text( + '${dataShalat?.timingsAsr}', + style: const TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + ], + ), ), - size: 24.0, - color: Colors.white, - ), - const Spacer(), - const Text( - 'Maghrib', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - const Spacer(), - Obx(() => Text( - '${controller.shalat.timingsMaghrib}', - style: const TextStyle( + Container( + padding: const EdgeInsets.symmetric( + vertical: 16.0, horizontal: 32.0), + margin: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 2.0), + decoration: ShapeDecoration( + color: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + )), + child: Row( + children: [ + const ImageIcon( + AssetImage( + AppAssets.iconShalatMaghrib, + ), + size: 24.0, color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - )), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 32.0), - margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), - decoration: ShapeDecoration( - color: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - )), - child: Row( - children: [ - const ImageIcon( - AssetImage( - AppAssets.iconShalatIsha, + ), + const SizedBox(width: 8), + const Text( + AppString.maghrib, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + const Spacer(), + Text( + '${dataShalat?.timingsMaghrib}', + style: const TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + ], + ), ), - size: 24.0, - color: Colors.white, - ), - const Spacer(), - const Text( - 'Isha', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - ), - const Spacer(), - Obx(() => Text( - '${controller.shalat.timingsIsha}', - style: const TextStyle( + Container( + padding: const EdgeInsets.symmetric( + vertical: 16.0, horizontal: 32.0), + margin: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 2.0), + decoration: ShapeDecoration( + color: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + )), + child: Row( + children: [ + const ImageIcon( + AssetImage( + AppAssets.iconShalatIsha, + ), + size: 24.0, color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold), - )), - ], + ), + const SizedBox(width: 8), + const Text( + AppString.isha, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + const Spacer(), + Text( + '${dataShalat?.timingsIsha}', + style: const TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + ], + ), + ), + ], + ); + }, + onError: (_) => const SizedBox.shrink(), + onLoading: ListView.separated( + itemCount: 5, + shrinkWrap: true, + physics: const ScrollPhysics(), + separatorBuilder: (BuildContext context, int index) { + return Container( + height: 4.0, + ); + }, + itemBuilder: (BuildContext context, int index) { + return const BoxPlaceholder(width: double.infinity); + }, ), - ), - ], - ); + ) + + // Obx(() { + // if (controller.status.isLoading) { + // return const BoxPlaceholder(width: double.infinity); + // } else if (controller.status.isError) { + // Get.snackbar( + // 'information'.tr, + // controller.status.errorMessage!, + // snackPosition: SnackPosition.BOTTOM, + // margin: const EdgeInsets.all(8.0), + // backgroundColor: AppColors.error, + // ); + // return const SizedBox.shrink(); + // } else if (controller.status.isSuccess) { + // final dataShalat = controller.state; + // return Column( + // children: [ + // Container( + // padding: const EdgeInsets.symmetric( + // vertical: 16.0, horizontal: 32.0), + // margin: + // const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), + // decoration: ShapeDecoration( + // color: Colors.transparent, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(16.0), + // )), + // child: Row( + // children: [ + // const ImageIcon( + // AssetImage( + // AppAssets.iconShalatShubuh, + // ), + // size: 24.0, + // color: Colors.white, + // ), + // const Spacer(), + // const Text( + // AppString.shubuh, + // style: TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // const Spacer(), + // Text( + // '${dataShalat?.timingsFajr}', + // style: const TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // ], + // ), + // ), + // Container( + // padding: const EdgeInsets.symmetric( + // vertical: 16.0, horizontal: 32.0), + // margin: + // const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), + // decoration: ShapeDecoration( + // color: Colors.transparent, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(16.0), + // )), + // child: Row( + // children: [ + // const ImageIcon( + // AssetImage( + // AppAssets.iconShalatDzuhr, + // ), + // size: 24.0, + // color: Colors.white, + // ), + // const Spacer(), + // const Text( + // AppString.dzuhr, + // style: TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // const Spacer(), + // Text( + // '${dataShalat?.timingsDhuhr}', + // style: const TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // ], + // ), + // ), + // Container( + // padding: const EdgeInsets.symmetric( + // vertical: 16.0, horizontal: 32.0), + // margin: + // const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), + // decoration: ShapeDecoration( + // color: Colors.transparent, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(16.0), + // )), + // child: Row( + // children: [ + // const ImageIcon( + // AssetImage( + // AppAssets.iconShalatAshr, + // ), + // size: 24.0, + // color: Colors.white, + // ), + // const Spacer(), + // const Text( + // AppString.ashr, + // style: TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // const Spacer(), + // Text( + // '${dataShalat?.timingsAsr}', + // style: const TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // ], + // ), + // ), + // Container( + // padding: const EdgeInsets.symmetric( + // vertical: 16.0, horizontal: 32.0), + // margin: + // const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), + // decoration: ShapeDecoration( + // color: Colors.transparent, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(16.0), + // )), + // child: Row( + // children: [ + // const ImageIcon( + // AssetImage( + // AppAssets.iconShalatMaghrib, + // ), + // size: 24.0, + // color: Colors.white, + // ), + // const Spacer(), + // const Text( + // AppString.maghrib, + // style: TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // const Spacer(), + // Text( + // '${dataShalat?.timingsMaghrib}', + // style: const TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // ], + // ), + // ), + // Container( + // padding: const EdgeInsets.symmetric( + // vertical: 16.0, horizontal: 32.0), + // margin: + // const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0), + // decoration: ShapeDecoration( + // color: Colors.transparent, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(16.0), + // )), + // child: Row( + // children: [ + // const ImageIcon( + // AssetImage( + // AppAssets.iconShalatIsha, + // ), + // size: 24.0, + // color: Colors.white, + // ), + // const Spacer(), + // const Text( + // AppString.isha, + // style: TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // const Spacer(), + // Text( + // '${dataShalat?.timingsIsha}', + // style: const TextStyle( + // color: Colors.white, + // fontSize: 16.0, + // fontWeight: FontWeight.bold), + // ), + // ], + // ), + // ), + // ], + // ); + // } else { + // return const SizedBox.shrink(); + // } + // }), + ); } } diff --git a/lib/app/modules/surah/bindings/surah_binding.dart b/lib/app/modules/surah/bindings/surah_binding.dart index a37c3b8..3da66c5 100644 --- a/lib/app/modules/surah/bindings/surah_binding.dart +++ b/lib/app/modules/surah/bindings/surah_binding.dart @@ -1,4 +1,7 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:get/get.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_ayah_by_surah_number_usecase.dart'; import 'package:suji/app/modules/surah/controllers/audio_controller.dart'; import '../controllers/surah_controller.dart'; @@ -6,14 +9,14 @@ import '../controllers/surah_controller.dart'; class SurahBinding extends Bindings { @override void dependencies() { - Get.put( - SurahController(), + Get.lazyPut( + () => SurahController( + getAyahBySurahNumberUsecase: Get.find()), ); + Get.lazyPut(() => GetAyahBySurahNumberUsecase( + surahRepository: Get.find())); Get.lazyPut( - () => AudioController(), + () => AudioController(connectivity: Get.find()), ); - // Get.lazyPut( - // () => SurahProvider(), - // ); } } diff --git a/lib/app/modules/surah/controllers/audio_controller.dart b/lib/app/modules/surah/controllers/audio_controller.dart index f2f4e2c..e9e54f5 100644 --- a/lib/app/modules/surah/controllers/audio_controller.dart +++ b/lib/app/modules/surah/controllers/audio_controller.dart @@ -2,13 +2,13 @@ import 'dart:async'; import 'package:audioplayers/audioplayers.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:suji/core/theme/colors.dart'; +import 'package:suji/app/widgets/custom_snackbar.dart'; +import 'package:suji/core/values/constant.dart'; class AudioController extends GetxController { + Connectivity connectivity; final AudioPlayer _audioPlayer = AudioPlayer() - // ..setPlayerMode(PlayerMode.lowLatency) ..setReleaseMode(ReleaseMode.stop); AudioPlayer get audioPlayer => _audioPlayer; // final _playerState = PlayerState.stopped.obs; @@ -23,12 +23,11 @@ class AudioController extends GetxController { late StreamSubscription _positionSubscription; late StreamSubscription _playerCompleteSubscription; late StreamSubscription _playerStateSubscription; + AudioController({required this.connectivity}); @override void onInit() { super.onInit(); - // print('Duration: ${_audioPlayer.getDuration()}'); - // print('Position: ${_audioPlayer.getCurrentPosition()}'); setupAudioPlayer(); } @@ -63,10 +62,6 @@ class AudioController extends GetxController { audioPlayer.onLog.listen((String message) { AudioLogger.log(message); }, onError: (Object e, [StackTrace? stackTrace]) { - Get.snackbar('Information', e.toString(), - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error); AudioLogger.error(e, stackTrace); }); } @@ -75,29 +70,21 @@ class AudioController extends GetxController { if (selectedAudioIdx == index && (audioPlayer.state == PlayerState.playing || audioPlayer.state == PlayerState.paused)) { - // print('stop aja'); await audioPlayer.stop(); resetSelectedAudioIdx(); } else { await audioPlayer.stop(); try { - final hasConnected = await Connectivity().checkConnectivity(); + final hasConnected = await connectivity.checkConnectivity(); if (hasConnected == ConnectivityResult.mobile || hasConnected == ConnectivityResult.wifi) { await audioPlayer.play(UrlSource(audioSource)); _selectedAudioIdx.value = index; } else { - Get.snackbar( - 'Information', 'Play Audio must be connect to internet!!!', - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error); + showErrorMessage('audio_play_internet'.tr); } } catch (e) { - Get.snackbar('Information', 'Check your connection', - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error); + showErrorMessage(AppString.socketException); } } } diff --git a/lib/app/modules/surah/controllers/surah_controller.dart b/lib/app/modules/surah/controllers/surah_controller.dart index 294d7b5..af480c6 100644 --- a/lib/app/modules/surah/controllers/surah_controller.dart +++ b/lib/app/modules/surah/controllers/surah_controller.dart @@ -1,39 +1,45 @@ -import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:suji/app/domain/entities/surah_detail.dart'; -import 'package:suji/app/domain/repository/surah_repository.dart'; -import 'package:suji/core/theme/colors.dart'; -import 'package:suji/core/utils/base_state.dart'; +import 'package:suji/app/domain/usescases/get_ayah_by_surah_number_usecase.dart'; +import 'package:suji/app/widgets/custom_snackbar.dart'; -class SurahController extends GetxController { - final SurahRepository surahRepository = Get.find(); +class SurahController extends GetxController + with StateMixin> { + final GetAyahBySurahNumberUsecase getAyahBySurahNumberUsecase; - final _state = BaseState.empty.obs; - BaseState get state => _state.value; + SurahController({required this.getAyahBySurahNumberUsecase}); - final List _listAyat = [].obs; - List get listAyat => _listAyat; + // final _state = BaseState.empty.obs; + // BaseState get state => _state.value; + + // final _message = ''.obs; + // String get message => _message.value; + + // final List _listAyat = [].obs; + // List get listAyat => _listAyat; @override - void onInit() { + void onInit() async { super.onInit(); - getAyahBySurahNumber(); + await getAyahBySurahNumber(); } + /// get all ayah by surah number Future getAyahBySurahNumber() async { - _state.value = BaseState.loading; - final result = await surahRepository - .getAyahBySurahNumber(int.parse(Get.parameters['number']!)); + // _state.value = BaseState.loading; + change([], status: RxStatus.loading()); + final result = await getAyahBySurahNumberUsecase + .invoke(int.parse(Get.parameters['number']!)); result.fold((failure) { - _state.value = BaseState.error; - Get.snackbar('Information', failure.message, - snackPosition: SnackPosition.BOTTOM, - margin: const EdgeInsets.all(8.0), - backgroundColor: AppColors.error); + // _message.value = failure.message; + // _state.value = BaseState.error; + showErrorMessage(failure.message); + change([], status: RxStatus.error(failure.message)); }, (surahData) { - _listAyat.assignAll(surahData); - _state.value = BaseState.success; + // _listAyat.assignAll(surahData); + // _state.value = BaseState.success; + change(surahData, status: RxStatus.success()); }); } } diff --git a/lib/app/modules/surah/views/surah_view.dart b/lib/app/modules/surah/views/surah_view.dart index 9670b8b..afd5017 100644 --- a/lib/app/modules/surah/views/surah_view.dart +++ b/lib/app/modules/surah/views/surah_view.dart @@ -6,9 +6,7 @@ import 'package:suji/app/domain/entities/surah_detail.dart'; import 'package:suji/app/modules/surah/controllers/audio_controller.dart'; import 'package:suji/core/theme/colors.dart'; import 'package:suji/core/theme/text_style.dart'; -import 'package:suji/core/utils/base_state.dart'; import 'package:suji/app/widgets/box_placeholder.dart'; -import 'package:suji/core/utils/logger.dart'; import 'package:suji/core/values/constant.dart'; import '../controllers/surah_controller.dart'; @@ -20,16 +18,60 @@ class SurahView extends GetView { Widget build(BuildContext context) { ScreenUtil.init(context, designSize: const Size(375, 812)); return Scaffold( + appBar: AppBar( + title: const Text( + AppString.surah, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + leading: IconButton( + onPressed: () => Get.back(), + icon: const Icon( + Icons.arrow_back_ios_new, + color: AppColors.background, + size: 24.0, + ), + ), + actions: [ + controller.obx((state) { + return IconButton( + onPressed: () => _showTafsirBottomsheet(state!.first.tafsirId), + icon: Icon( + Icons.info_outline, + size: 24.w, + color: Colors.white, + ), + ); + }, onError: (_) => const SizedBox.shrink()), + // Obx(() { + // if (controller.status == RxStatus.success()) { + // return IconButton( + // onPressed: () => + // _showTafsirBottomsheet(controller.state!.first.tafsirId), + // icon: Icon( + // Icons.info_outline, + // size: 24.w, + // color: Colors.white, + // ), + // ); + // } else { + // return Container(); + // } + // }), + ], + ), body: SafeArea( child: SingleChildScrollView( child: Padding( padding: EdgeInsets.symmetric(horizontal: 24.w), child: Column( children: [ - SizedBox( - height: 45.h, - ), - _buildNavBar(), + // SizedBox( + // height: 45.h, + // ), + // _buildNavBar(), SizedBox( height: 24.h, ), @@ -47,159 +89,292 @@ class SurahView extends GetView { } Widget _buildAyahList() { - return Obx(() { - if (controller.state == BaseState.loading) { - return ListView.separated( - itemCount: 10, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (BuildContext context, int index) { - return Container( - height: 4.h, - ); - }, - itemBuilder: (BuildContext context, int index) { - return BoxPlaceholder( - width: double.infinity, - height: 64.h, - ); - }, - ); - } else if (controller.state == BaseState.error) { - return Center( - child: IconButton( - onPressed: () async { - await controller.getAyahBySurahNumber(); - }, - icon: Icon( - Icons.replay, - size: 24.h, - ), - ), - ); - } else if (controller.state == BaseState.success && - controller.listAyat.isNotEmpty) { - return ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: controller.listAyat.length, - itemBuilder: (context, index) { - final ayah = controller.listAyat[index]; - return SizedBox( - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - decoration: BoxDecoration( - color: AppColors.secondary, - borderRadius: BorderRadius.all( - Radius.circular(10.r), - ), - ), - child: Padding( - padding: EdgeInsets.all(10.w), - child: Row( - children: [ - CircleAvatar( - radius: 16.r, - backgroundColor: AppColors.primaryContainer, - child: Text( - '${ayah.versesNumberInSurah}', - style: TextStyle( - fontSize: 14.sp, - fontWeight: medium, - color: Colors.white, - ), - ), - ), - const Spacer(), - IconButton( - tooltip: 'Bagikan', - onPressed: () { - final textAyah = ''' - Surah : ${ayah.nameTransliterationId} - Ayah : ${ayah.versesNumberInSurah} - ${ayah.versesTextArab} - Tafsir: - ${ayah.versesTafsirIdShort}'''; - Share.share(textAyah); - }, - icon: const Icon( - Icons.share, - color: AppColors.primary, - ), - ), - AudioPlayerWidget( - index: index, - ayah: ayah, - ), - IconButton( - tooltip: 'Tafsir', - onPressed: () => _showTafsirBottomsheet( - ayah.versesTafsirIdShort), - icon: const Icon( - Icons.document_scanner, - color: AppColors.primary, - ), + return controller.obx(_buildAyahListSuccess, + onError: _buildAyahListError, + onLoading: _buildAyahListLoading(), + onEmpty: const SizedBox.shrink()); + // return Obx(() { + // if (controller.state == BaseState.loading) { + // return ListView.separated( + // itemCount: 10, + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // separatorBuilder: (BuildContext context, int index) { + // return Container( + // height: 4.h, + // ); + // }, + // itemBuilder: (BuildContext context, int index) { + // return BoxPlaceholder( + // width: double.infinity, + // height: 64.h, + // ); + // }, + // ); + // } else if (controller.state == BaseState.error) { + // Get.snackbar('information'.tr, controller.message, + // snackPosition: SnackPosition.BOTTOM, + // margin: const EdgeInsets.all(8.0), + // backgroundColor: AppColors.error); + // return Center( + // child: IconButton( + // onPressed: () async => await controller.getAyahBySurahNumber(), + // icon: Icon( + // Icons.replay, + // size: 24.h, + // ), + // ), + // ); + // } else if (controller.state == BaseState.success && + // controller.listAyat.isNotEmpty) { + // return ListView.builder( + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // itemCount: controller.listAyat.length, + // itemBuilder: (context, index) { + // final ayah = controller.listAyat[index]; + // return SizedBox( + // width: double.infinity, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Container( + // decoration: BoxDecoration( + // color: AppColors.secondary, + // borderRadius: BorderRadius.all( + // Radius.circular(10.r), + // ), + // ), + // child: Padding( + // padding: EdgeInsets.all(10.w), + // child: Row( + // children: [ + // CircleAvatar( + // radius: 16.r, + // backgroundColor: AppColors.primaryContainer, + // child: Text( + // '${ayah.versesNumberInSurah}', + // style: TextStyle( + // fontSize: 14.sp, + // fontWeight: medium, + // color: Colors.white, + // ), + // ), + // ), + // const Spacer(), + // IconButton( + // tooltip: 'Bagikan', + // onPressed: () { + // final textAyah = + // ''' + // Surah : ${ayah.nameTransliterationId} + // Ayah : ${ayah.versesNumberInSurah} + // ${ayah.versesTextArab} + // Tafsir: + // ${ayah.versesTafsirIdShort}'''; + // Share.share(textAyah); + // }, + // icon: const Icon( + // Icons.share, + // color: AppColors.primary, + // ), + // ), + // AudioPlayerWidget( + // index: index, + // ayah: ayah, + // ), + // IconButton( + // tooltip: 'Tafsir', + // onPressed: () => _showTafsirBottomsheet( + // ayah.versesTafsirIdShort), + // icon: const Icon( + // Icons.document_scanner, + // color: AppColors.primary, + // ), + // ), + // ], + // ), + // ), + // ), + // SizedBox( + // height: 24.h, + // ), + // Align( + // alignment: Alignment.centerRight, + // child: Text( + // ayah.versesTextArab, + // textDirection: TextDirection.rtl, + // style: TextStyle( + // fontSize: 18.sp, fontWeight: FontWeight.w700), + // ), + // ), + // SizedBox( + // height: 16.h, + // ), + // Text( + // ayah.versesTranslationId, + // textAlign: TextAlign.justify, + // style: + // TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w400), + // ), + // SizedBox( + // height: 16.h, + // ), + // ], + // ), + // ); + // }, + // ); + // } else { + // Log.d('[SurahView][controller.state]', controller.state.toString()); + // Log.d('[SurahView][controller.listAyat.isNotEmpty]', + // controller.listAyat.isNotEmpty.toString()); + // return Center( + // child: IconButton( + // onPressed: () async => await controller.getAyahBySurahNumber(), + // icon: Icon( + // Icons.replay, + // size: 24.h, + // ), + // ), + // ); + // } + // }); + } + + Widget _buildAyahListSuccess(listAyah) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: listAyah?.length, + itemBuilder: (context, index) { + final ayah = listAyah![index]; + return SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: AppColors.secondary, + borderRadius: BorderRadius.all( + Radius.circular(10.r), + ), + ), + child: Padding( + padding: EdgeInsets.all(10.w), + child: Row( + children: [ + CircleAvatar( + radius: 16.r, + backgroundColor: AppColors.primaryContainer, + child: Text( + '${ayah.versesNumberInSurah}', + style: TextStyle( + fontSize: 14.sp, + fontWeight: medium, + color: Colors.white, ), - ], + ), ), - ), - ), - SizedBox( - height: 24.h, - ), - Align( - alignment: Alignment.centerRight, - child: Text( - ayah.versesTextArab, - textDirection: TextDirection.rtl, - style: TextStyle( - fontSize: 18.sp, fontWeight: FontWeight.w700), - ), - ), - SizedBox( - height: 16.h, - ), - Text( - ayah.versesTranslationId, - textAlign: TextAlign.justify, - style: - TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w400), - ), - SizedBox( - height: 16.h, + const Spacer(), + IconButton( + tooltip: 'Bagikan', + onPressed: () { + final textAyah = ''' + Surah : ${ayah.nameTransliterationId} + Ayah : ${ayah.versesNumberInSurah} + ${ayah.versesTextArab} + Tafsir: + ${ayah.versesTafsirIdShort}'''; + Share.share(textAyah); + }, + icon: const Icon( + Icons.share, + color: AppColors.primary, + ), + ), + AudioPlayerWidget( + index: index, + ayah: ayah, + ), + IconButton( + tooltip: 'Tafsir', + onPressed: () => + _showTafsirBottomsheet(ayah.versesTafsirIdShort), + icon: const Icon( + Icons.document_scanner, + color: AppColors.primary, + ), + ), + ], ), - ], + ), ), - ); - }, - ); - } else { - Log.d('[SurahView][controller.state]', controller.state.toString()); - Log.d('[SurahView][controller.listAyat.isNotEmpty]', - controller.listAyat.isNotEmpty.toString()); - // print(controller.state); - // print('isi listAyat: ${controller.listAyat.isNotEmpty}'); - return Center( - child: IconButton( - onPressed: () async { - await controller.getAyahBySurahNumber(); - }, - icon: Icon( - Icons.replay, - size: 24.h, - ), + SizedBox( + height: 24.h, + ), + Align( + alignment: Alignment.centerRight, + child: Text( + ayah.versesTextArab, + textDirection: TextDirection.rtl, + style: + TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), + ), + ), + SizedBox( + height: 16.h, + ), + Text( + ayah.versesTranslationId, + textAlign: TextAlign.justify, + style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w400), + ), + SizedBox( + height: 16.h, + ), + ], ), ); - } - }); + }, + ); + } + + Widget _buildAyahListError(errorMessage) { + return Center( + child: IconButton( + onPressed: () async => await controller.getAyahBySurahNumber(), + icon: Icon( + Icons.replay, + size: 24.h, + ), + ), + ); + } + + ListView _buildAyahListLoading() { + return ListView.separated( + itemCount: 10, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + separatorBuilder: (BuildContext context, int index) { + return Container( + height: 4.h, + ); + }, + itemBuilder: (BuildContext context, int index) { + return BoxPlaceholder( + width: double.infinity, + height: 64.h, + ); + }, + ); } Widget _buildBanner() { return Container( - width: double.infinity, + // width: double.infinity, decoration: BoxDecoration( image: const DecorationImage( fit: BoxFit.fill, @@ -231,80 +406,152 @@ class SurahView extends GetView { ), ), child: Padding( - padding: EdgeInsets.symmetric(vertical: 28.h), - child: Obx(() { - if (controller.state == BaseState.success && - controller.listAyat.isNotEmpty) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - controller.listAyat.first.nameTransliterationId, - style: TextStyle( - fontSize: 26.sp, - fontWeight: FontWeight.w500, - color: Colors.white, + padding: EdgeInsets.symmetric(vertical: 28.h), + child: controller.obx( + (listAyah) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '${listAyah?.first.nameTransliterationId}', + style: TextStyle( + fontSize: 26.sp, + fontWeight: FontWeight.w500, + color: Colors.white, + ), ), - ), - SizedBox( - height: 4.h, - ), - Text( - controller.listAyat.first.nameTranslationId, - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w500, - color: Colors.white, + SizedBox( + height: 4.h, ), - ), - SizedBox( - height: 16.h, - ), - Row( - children: [ - SizedBox( - width: 63.w, + Text( + '${listAyah?.first.nameTranslationId}', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w500, + color: Colors.white, ), - const Expanded( - child: Divider( - color: Colors.white, + ), + SizedBox( + height: 16.h, + ), + Row( + children: [ + SizedBox( + width: 63.w, ), + const Expanded( + child: Divider( + color: Colors.white, + ), + ), + SizedBox( + width: 63.w, + ), + ], + ), + SizedBox( + height: 16.h, + ), + Text( + '${listAyah?.first.relevationId} - ${listAyah?.first.numberOfVerses} ayat', + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + color: Colors.white, ), - SizedBox( - width: 63.w, - ), - ], - ), - SizedBox( - height: 16.h, - ), - Text( - '${controller.listAyat.first.relevationId} - ${controller.listAyat.first.numberOfVerses} ayat', - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w500, - color: Colors.white, ), - ), - SizedBox( - height: 32.sp, - ), - Text( - 'بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ', - style: TextStyle( - fontSize: 32.sp, - fontWeight: FontWeight.w500, - color: Colors.white, + SizedBox( + height: 32.sp, ), - ), - ], - ); - } else { - return Container(); - } - }), - ), + Text( + 'بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ', + style: TextStyle( + fontSize: 32.sp, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ], + ); + }, + onError: (error) => const SizedBox.shrink(), + onLoading: const SizedBox.shrink(), + onEmpty: const SizedBox.shrink(), + ) + // Obx(() { + // if (controller.state == BaseState.success && + // controller.listAyat.isNotEmpty) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text( + // controller.listAyat.first.nameTransliterationId, + // style: TextStyle( + // fontSize: 26.sp, + // fontWeight: FontWeight.w500, + // color: Colors.white, + // ), + // ), + // SizedBox( + // height: 4.h, + // ), + // Text( + // controller.listAyat.first.nameTranslationId, + // style: TextStyle( + // fontSize: 16.sp, + // fontWeight: FontWeight.w500, + // color: Colors.white, + // ), + // ), + // SizedBox( + // height: 16.h, + // ), + // Row( + // children: [ + // SizedBox( + // width: 63.w, + // ), + // const Expanded( + // child: Divider( + // color: Colors.white, + // ), + // ), + // SizedBox( + // width: 63.w, + // ), + // ], + // ), + // SizedBox( + // height: 16.h, + // ), + // Text( + // '${controller.listAyat.first.relevationId} - ${controller.listAyat.first.numberOfVerses} ayat', + // style: TextStyle( + // fontSize: 14.sp, + // fontWeight: FontWeight.w500, + // color: Colors.white, + // ), + // ), + // SizedBox( + // height: 32.sp, + // ), + // Text( + // 'بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ', + // style: TextStyle( + // fontSize: 32.sp, + // fontWeight: FontWeight.w500, + // color: Colors.white, + // ), + // ), + // ], + // ); + // } else { + // return Container(); + // } + // }), + ), ), ); } @@ -365,59 +612,59 @@ class SurahView extends GetView { ); } - Widget _buildNavBar() { - return Row( - children: [ - IconButton( - onPressed: () => Get.back(), - tooltip: 'Kembali', - icon: Icon( - Icons.arrow_back, - size: 24.w, - ), - ), - Obx(() { - if (controller.state == BaseState.loading) { - return BoxPlaceholder( - height: 24.h, - ); - } else if (controller.state == BaseState.error) { - return Text( - '-', - style: TextStyle( - fontSize: 20.sp, - ), - ); - } else if (controller.state == BaseState.success) { - return Text( - '${controller.listAyat.firstOrNull?.nameTransliterationId}', - style: TextStyle( - // color: textColor1, - fontSize: 20.sp, - fontWeight: FontWeight.bold, - ), - ); - } else { - return Text( - '-', - style: TextStyle( - fontSize: 20.sp, - ), - ); - } - }), - const Spacer(), - IconButton( - onPressed: () {}, - icon: Icon( - Icons.info_outline, - size: 24.w, - color: AppColors.primaryContainer, - ), - ), - ], - ); - } + // Widget _buildNavBar() { + // return Row( + // children: [ + // IconButton( + // onPressed: () => Get.back(), + // tooltip: 'Kembali', + // icon: Icon( + // Icons.arrow_back, + // size: 24.w, + // ), + // ), + // Obx(() { + // if (controller.state == BaseState.loading) { + // return BoxPlaceholder( + // height: 24.h, + // ); + // } else if (controller.state == BaseState.error) { + // return Text( + // '-', + // style: TextStyle( + // fontSize: 20.sp, + // ), + // ); + // } else if (controller.state == BaseState.success) { + // return Text( + // '${controller.listAyat.firstOrNull?.nameTransliterationId}', + // style: TextStyle( + // // color: textColor1, + // fontSize: 20.sp, + // fontWeight: FontWeight.bold, + // ), + // ); + // } else { + // return Text( + // '-', + // style: TextStyle( + // fontSize: 20.sp, + // ), + // ); + // } + // }), + // const Spacer(), + // IconButton( + // onPressed: () {}, + // icon: Icon( + // Icons.info_outline, + // size: 24.w, + // color: AppColors.primaryContainer, + // ), + // ), + // ], + // ); + // } } class AudioPlayerWidget extends GetView { @@ -434,9 +681,8 @@ class AudioPlayerWidget extends GetView { Widget build(BuildContext context) { return IconButton( tooltip: 'Play/Stop', - onPressed: () async { - await controller.onAudioPressed(index, ayah.versesAudioPrimary); - }, + onPressed: () async => + await controller.onAudioPressed(index, ayah.versesAudioPrimary), icon: Obx(() => Icon( controller.selectedAudioIdx == index ? Icons.pause_circle_outline diff --git a/lib/app/modules/tasbih/bindings/tasbih_binding.dart b/lib/app/modules/tasbih/bindings/tasbih_binding.dart new file mode 100644 index 0000000..6ef2f40 --- /dev/null +++ b/lib/app/modules/tasbih/bindings/tasbih_binding.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import 'package:suji/app/modules/tasbih/controllers/tasbih_controller.dart'; + +class TasbihBinding implements Bindings { + @override + void dependencies() { + Get.lazyPut(() => TasbihController()); + } +} diff --git a/lib/app/modules/tasbih/controllers/tasbih_controller.dart b/lib/app/modules/tasbih/controllers/tasbih_controller.dart new file mode 100644 index 0000000..f5394b6 --- /dev/null +++ b/lib/app/modules/tasbih/controllers/tasbih_controller.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class TasbihController extends GetxController { + TextEditingController textEditingController = TextEditingController(); + final _target = 11.obs; + set target(value) { + _target.value = value; + textEditingController.text = '$target'; + } + + int get target => _target.value; + + final _counter = 0.obs; + set counter(value) { + if (value >= 0 && value <= target) { + _counter.value = value; + } + } + + int get counter => _counter.value; + + @override + void onInit() { + super.onInit(); + textEditingController.text = target.toString(); + } + + @override + void dispose() { + textEditingController.dispose(); + super.dispose(); + } + + onChangedTextField(String value) { + if (value.isEmpty || value == '00') { + target = 0; + } else { + target = int.parse(value); + } + } +} diff --git a/lib/app/modules/tasbih/views/tasbih_view.dart b/lib/app/modules/tasbih/views/tasbih_view.dart new file mode 100644 index 0000000..f84c0bc --- /dev/null +++ b/lib/app/modules/tasbih/views/tasbih_view.dart @@ -0,0 +1,413 @@ +import 'package:get/get.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:suji/app/modules/tasbih/controllers/tasbih_controller.dart'; +import 'package:suji/core/theme/colors.dart'; + +class TasbihView extends GetView { + const TasbihView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text( + 'Tasbih Digital', + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + leading: IconButton( + onPressed: () => Get.back(), + icon: const Icon( + Icons.arrow_back_ios_new, + color: AppColors.background, + size: 24.0, + ), + ), + ), + body: SafeArea( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.all(8.0), + // color: Colors.green, + child: Center( + child: Obx(() => Text('${controller.counter}', + style: Get.textTheme.displayLarge?.copyWith( + fontSize: 46.0, + color: controller.counter >= controller.target + ? Colors.red + : null))), + ), + ), + _buildCounterButton(), + const SizedBox( + height: 8.0, + ), + Flexible( + fit: FlexFit.loose, + child: Container( + width: double.infinity, + decoration: + const BoxDecoration(color: Colors.white, boxShadow: [ + BoxShadow( + blurRadius: 8.0, + offset: Offset(0.0, -6.0), + color: Colors.black12, + ) + ]), + padding: const EdgeInsets.only( + top: 16.0, + bottom: 16.0, + ), + child: Text('tasbih_description'.tr, + textAlign: TextAlign.center, + style: Get.textTheme.titleMedium), + ), + ), + Flexible( + fit: FlexFit.loose, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + // color: Colors.red, + margin: const EdgeInsets.only( + left: 16.0, + ), + child: Text('goal'.tr, + textAlign: TextAlign.start, + style: Get.textTheme.titleLarge), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 8), + // color: Colors.orange, + child: TextFormField( + controller: controller.textEditingController, + onChanged: controller.onChangedTextField, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], + decoration: InputDecoration( + fillColor: Colors.black12, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12.0), + borderSide: const BorderSide( + color: Colors.black12, + ), + ), + // helperText: 'Masukkan targetmu', + ), + ), + ), + ], + ), + ), + Flexible( + fit: FlexFit.loose, + child: Container( + // height: 100.0, + padding: const EdgeInsets.all(8.0), + // color: Colors.red, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: InkWell( + key: const Key('TargetButton11'), + onTap: () { + controller.target = 11; + }, + child: Obx(() => Container( + padding: const EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: controller.target == 11 + ? AppColors.primary + : Colors.grey, + borderRadius: BorderRadius.circular(12.0), + ), + child: const Text( + '11', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + )), + ), + ), + const SizedBox( + width: 8.0, + ), + Expanded( + child: InkWell( + key: const Key('TargetButton33'), + onTap: () { + controller.target = 33; + }, + child: Obx(() => Container( + padding: const EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: controller.target == 33 + ? AppColors.primary + : Colors.grey, + borderRadius: BorderRadius.circular(12.0), + ), + child: const Text( + '33', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + )), + ), + ), + const SizedBox( + width: 8.0, + ), + Expanded( + child: InkWell( + key: const Key('TargetButton99'), + onTap: () { + controller.target = 99; + }, + child: Obx(() => Container( + padding: const EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: controller.target == 99 + ? AppColors.primary + : Colors.grey, + borderRadius: BorderRadius.circular(12.0), + ), + child: const Text( + '99', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + )), + ), + ), + ], + ), + ), + ), + ), + // Flexible( + // fit: FlexFit.loose, + // child: Container( + // height: 100.0, + // color: AppColors.primary, + // ), + // ), + ], + ), + ), + ), + ); + } + + Widget _buildCounterButton() { + return SizedBox( + height: 275.0, + child: + Stack(fit: StackFit.expand, alignment: Alignment.center, children: [ + Positioned( + left: 0, + right: 0, + child: ElevatedButton( + key: const Key('IncreaseButton'), + onPressed: () { + controller.counter++; + }, + style: ButtonStyle( + shadowColor: MaterialStateProperty.all(Colors.black), + shape: MaterialStateProperty.all(const CircleBorder( + side: BorderSide(color: AppColors.onPrimary, width: 2.0)))), + child: const Icon( + Icons.keyboard_arrow_up, + size: 200.0, + color: AppColors.background, + ), + ), + ), + Positioned( + bottom: 0, + child: ElevatedButton( + key: const Key('DecreaseButton'), + onPressed: () { + controller.counter--; + }, + style: ButtonStyle( + shape: MaterialStateProperty.all(const CircleBorder( + side: + BorderSide(color: AppColors.background, width: 2.0)))), + child: const Icon( + Icons.keyboard_arrow_down, + color: AppColors.background, + size: 25.0, + ), + ), + ), + Positioned( + top: 0, + child: ElevatedButton( + key: const Key('ResetButton'), + onPressed: () { + controller.counter = 0; + }, + style: ButtonStyle( + shape: MaterialStateProperty.all(const CircleBorder( + side: + BorderSide(color: AppColors.background, width: 2.0)))), + child: const Icon( + Icons.restart_alt, + color: AppColors.background, + size: 25.0, + ), + ), + ), + ]), + ); + } + + // Stack _buildHeroItem() { + // return Stack( + // // fit: StackFit.expand, + // children: [ + // Container( + // height: Get.height * 0.2, + // decoration: const BoxDecoration( + // color: Colors.orange, + // ), + // ), + // Transform.translate( + // offset: const Offset(0.0, 100), + // child: Center( + // child: Container( + // width: Get.width * 0.8, + // padding: const EdgeInsets.all(8.0), + // decoration: const BoxDecoration( + // color: Colors.white, + // boxShadow: [ + // BoxShadow( + // color: Colors.black12, + // blurRadius: 16.0, + // offset: Offset(.0, 6.0)) + // ], + // borderRadius: BorderRadius.all( + // Radius.circular( + // 8.0, + // ), + // ), + // ), + // child: SingleChildScrollView( + // scrollDirection: Axis.horizontal, + // controller: ScrollController(), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Container( + // width: 100, + // color: Colors.green, + // child: const Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.photo_camera_outlined, + // size: 20.0, + // color: Colors.red, + // ), + // Text( + // 'Jadwal Shalat', + // maxLines: 2, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // Container( + // width: 100, + // color: Colors.green, + // child: const Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.photo_camera_outlined, + // size: 20.0, + // color: Colors.red, + // ), + // Text( + // 'Jadwal Shalat', + // maxLines: 2, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // Container( + // width: 100, + // color: Colors.green, + // child: const Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.photo_camera_outlined, + // size: 20.0, + // color: Colors.red, + // ), + // Text( + // 'Jadwal Shalat', + // maxLines: 2, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // Container( + // width: 100, + // color: Colors.green, + // child: const Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.photo_camera_outlined, + // size: 20.0, + // color: Colors.red, + // ), + // Text( + // 'Jadwal Shalat', + // maxLines: 2, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // ], + // ), + // ), + // ), + // ), + // ), + // ], + // ); + // } +} diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 1dada9c..20f5c32 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -1,9 +1,15 @@ import 'package:get/get.dart'; +import 'package:suji/app/modules/asmaul_husna/bindings/asmaul_husna_binding.dart'; +import 'package:suji/app/modules/asmaul_husna/views/asmaul_husna_view.dart'; import 'package:suji/app/modules/lab_ui.dart'; +import 'package:suji/app/modules/menu/bindings/menu_binding.dart'; +import 'package:suji/app/modules/menu/views/menu_view.dart'; import 'package:suji/app/modules/search/bindings/search_binding.dart'; import 'package:suji/app/modules/search/views/search_view.dart'; import 'package:suji/app/modules/shalat/bindings/shalat_binding.dart'; import 'package:suji/app/modules/shalat/views/shalat_view.dart'; +import 'package:suji/app/modules/tasbih/bindings/tasbih_binding.dart'; +import 'package:suji/app/modules/tasbih/views/tasbih_view.dart'; import '../modules/home/bindings/home_binding.dart'; import '../modules/home/views/home_view.dart'; @@ -16,28 +22,49 @@ class AppPages { AppPages._(); static const INITIAL = Routes.HOME; + static const menu = '/menu'; + static const lab = '/lab'; + static const shalat = '/shalat'; + static const search = '/search'; + static const tasbih = '/tasbih'; + static const asmaulHusna = '/asmaul-husna'; static final routes = [ - GetPage(name: "/lab", page: () => const Lab()), + GetPage(name: lab, page: () => Lab()), GetPage( name: _Paths.HOME, page: () => const HomeView(), binding: HomeBinding(), ), GetPage( - name: "${_Paths.SURAH}/:number", + name: '${_Paths.SURAH}/:number', page: () => const SurahView(), binding: SurahBinding(), ), GetPage( - name: "/search", + name: search, page: () => const SearchView(), binding: SearchBinding(), ), GetPage( - name: "/shalat", + name: shalat, page: () => ShalatView(), binding: ShalatBinding(), ), + GetPage( + name: menu, + page: () => const MenuView(), + binding: MenuBinding(), + ), + GetPage( + name: tasbih, + page: () => const TasbihView(), + binding: TasbihBinding(), + ), + GetPage( + name: asmaulHusna, + page: () => const AsmaulHusnaView(), + binding: AsmaulHusnaBinding(), + ), ]; } diff --git a/lib/app/widgets/custom_snackbar.dart b/lib/app/widgets/custom_snackbar.dart new file mode 100644 index 0000000..cd9a3b7 --- /dev/null +++ b/lib/app/widgets/custom_snackbar.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:suji/core/theme/colors.dart'; + +showErrorMessage(String message) { + Get.snackbar( + 'information'.tr, + message, + snackPosition: SnackPosition.BOTTOM, + margin: const EdgeInsets.all(8.0), + backgroundColor: AppColors.error, + ); +} + +showSuccessMessage(String message) { + Get.snackbar( + 'information'.tr, + message, + snackPosition: SnackPosition.BOTTOM, + margin: const EdgeInsets.all(8.0), + backgroundColor: Colors.green, + ); +} diff --git a/lib/core/utils/base_state.dart b/lib/core/utils/base_state.dart index df62acc..58e630d 100644 --- a/lib/core/utils/base_state.dart +++ b/lib/core/utils/base_state.dart @@ -1 +1 @@ -enum BaseState { loading, success, error, empty } +// enum BaseState { loading, success, error, empty } diff --git a/lib/core/utils/date_utils.dart b/lib/core/utils/date_utils.dart index c388371..e4d9a46 100644 --- a/lib/core/utils/date_utils.dart +++ b/lib/core/utils/date_utils.dart @@ -5,7 +5,7 @@ class DateTimeUtils { const DateTimeUtils._(); /// get prayer time - static getPrayerTime(Shalat shalatTime) { + static DateTime getPrayerTime(Shalat shalatTime) { final now = DateTime.now(); final dateFormat = DateFormat('y/M/d'); final completeFormat = DateFormat('y/M/d H:m:s'); diff --git a/lib/core/utils/error_handler.dart b/lib/core/utils/error_handler.dart index 1b4c00e..092fae7 100644 --- a/lib/core/utils/error_handler.dart +++ b/lib/core/utils/error_handler.dart @@ -2,10 +2,6 @@ import 'package:get/get_connect/http/src/response/response.dart'; import 'package:suji/core/values/constant.dart'; dynamic errorHandler(Response response) { - // print(response.toString()); - // print(response.statusCode); - // print(response.status); - // print(response.body); switch (response.statusCode) { case 200: case 201: diff --git a/lib/core/values/constant.dart b/lib/core/values/constant.dart index 5fc4a54..e957b04 100644 --- a/lib/core/values/constant.dart +++ b/lib/core/values/constant.dart @@ -16,16 +16,28 @@ class AppAssets { // images & icons filepath constants static const imgBorderSurah = 'assets/images/border_surah.png'; static const imgBannerHome = 'assets/images/home_banner.png'; + static const imgMasjid = 'assets/images/masjid.jfif'; static const imgBannerShalat = 'assets/images/salah_banner.png'; static const iconShalatAshr = 'assets/images/Shalat-Ashar.png'; static const iconShalatIsha = 'assets/images/Shalat-Isya.png'; static const iconShalatMaghrib = 'assets/images/Shalat-Maghrib.png'; static const iconShalatShubuh = 'assets/images/Shalat-Shubuh.png'; static const iconShalatDzuhr = 'assets/images/Shalat-Zhuhur.png'; + static const iconPrayerTime = 'assets/images/icon-prayer-time.png'; + static const iconShalat = 'assets/images/icon-shalat.png'; + static const iconQuran = 'assets/images/icon-quran.png'; + static const iconTasbih = 'assets/images/icon-tasbih.png'; + + // data model json + static const jsonAsmaulHusna = 'assets/model/asmaul_husna.json'; + static const jsonDailyPray = 'assets/model/daily_pray.json'; } class AppString { AppString._(); + //Database call message + static const databaseError = 'Database Error'; + static const insertOrUpdateSuccess = 'Insert/Update Success'; //Api call error static const cancelRequest = 'Request to API server was cancelled'; @@ -48,5 +60,16 @@ class AppString { static const badGateway = 'Bad Gateway'; static const serviceUnavailable = 'Service Unavailable'; - // static const appFont = 'Poppins'; + // constant string + static const shubuh = 'Shubuh'; + static const dzuhr = 'Dzuhur'; + static const ashr = 'Ashr'; + static const maghrib = 'Maghrib'; + static const isha = 'Isha'; + static const tasbihDigital = 'Tasbih Digital'; + static const asmaulHusna = 'Asmaul Husna'; + static const alQuran = 'Al-Quran'; + static const alarm = 'Alarm'; + static const refresh = 'Refresh'; + static const surah = 'Surah'; } diff --git a/lib/core/values/internationalization.dart b/lib/core/values/internationalization.dart index 9a02313..91fb566 100644 --- a/lib/core/values/internationalization.dart +++ b/lib/core/values/internationalization.dart @@ -13,15 +13,42 @@ class Languages extends Translations { 'greeting': 'Assalamualaikum', 'user': 'Moslem Brother', 'back': 'back', - 'prayer time': 'prayer time', - 'shalat motivation': 'Different human who have iman between either are leaving shalat', + 'prayer_time': 'Prayer Time', + 'shalat_motivation': + 'Different human who have iman between either are leaving shalat', + 'information': 'Information', + 'alarm setup failed': 'Failed alarm setup', + 'alarm setup actived': 'Alarm has been actived', + 'alarm setup deactived': 'Alarm has been deactived', + 'daily_pray': 'Daily Pray', + 'daily_pray_reminder': 'Don\'t forget to praying', + 'search': 'Search', + 'last_read': 'Last Read', + 'goal': 'Goal', + 'tasbih_description': + 'Tasbih online, making it easy to count your Dhikr. Directly from your browser, no ads, no analytics, secure privacy and completely free.', + 'audio_play_internet': 'Play Audio must be connect to internet', }, 'id': { 'greeting': 'Assalamualaikum', 'user': 'Saudara Muslim', 'back': 'Kembali', - 'prayer time': 'Jadwal shalat', - 'shalat motivation': 'Yang membedakan antara orang beriman dengan tidak beriman adalah meninggalkan salat.', + 'prayer_time': 'Jadwal Shalat', + 'shalat_motivation': + 'Yang membedakan antara orang beriman dengan tidak beriman adalah meninggalkan salat.', + 'information': 'Informasi', + 'alarm setup failed': 'Gagal menjadwalkan alarm', + 'alarm setup actived': 'Alarm diaktifkan', + 'alarm setup deactived': 'Alarm dinonaktifka', + 'daily_pray': 'Doa Harian', + 'daily_pray_reminder': 'Jangan lupa baca doa', + 'search': 'Cari', + 'last_read': 'Terakhir Dibaca', + 'goal': 'Target', + 'tasbih_description': + 'Tasbih digital, mempermudah menghitung Dzikirmu. Langsung dari peramban, tanpa iklan, tanpa analitik, privasi aman dan gratis sepenuhnya', + 'audio_play_internet': + 'Pastikan terhubung dengan internet untuk memutar audio', }, }; -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index d7b2f4f..903109f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,7 +22,7 @@ Future main() async { /// openBox for adzan alarm settings on [NotificationsController] final document = await getApplicationDocumentsDirectory(); Hive.init(document.path); - await Hive.openBox('alarmSettings'); + await Hive.openBox('sujiSettingsBox'); /// Initialize the background service final backgroundService = BackgroundService(); @@ -36,12 +36,13 @@ Future main() async { runApp( GetMaterialApp( translations: Languages(), - locale: Get.deviceLocale, + locale: const Locale('id'), fallbackLocale: const Locale('en', 'US'), theme: appTheme, title: 'Application', - initialRoute: AppPages.INITIAL, - // "/lab", + initialRoute: + // AppPages.INITIAL, + AppPages.menu, getPages: AppPages.routes, debugShowCheckedModeBanner: false, ), diff --git a/pubspec.lock b/pubspec.lock index f521a90..7b452ba 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -97,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: @@ -632,6 +640,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + mockito: + dependency: "direct dev" + description: + name: mockito + sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616" + url: "https://pub.dev" + source: hosted + version: "5.4.2" nm: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3433564..4aca1de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: rxdart: ^0.27.7 hive: ^2.2.3 logger: ^2.0.1 + auto_size_text: ^3.0.0 dev_dependencies: build_runner: ^2.4.6 @@ -38,6 +39,7 @@ dev_dependencies: flutter_test: sdk: flutter isar_generator: ^3.1.0+1 + mockito: ^5.4.2 dependency_overrides: analyzer: ^6.0.0 @@ -54,5 +56,7 @@ flutter: weight: 700 assets: - assets/images/ + - assets/model/daily_pray.json + - assets/model/asmaul_husna.json uses-material-design: true diff --git a/test/app/data/datasources/shalat_local_data_source_test.dart b/test/app/data/datasources/shalat_local_data_source_test.dart new file mode 100644 index 0000000..3cd2239 --- /dev/null +++ b/test/app/data/datasources/shalat_local_data_source_test.dart @@ -0,0 +1,182 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:intl/intl.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/shalat_time_response.dart'; +import 'package:suji/app/data/datasources/database_helper.dart'; +import 'package:suji/app/data/datasources/shalat_local_data_source.dart'; +import 'package:suji/app/domain/entities/shalat.dart'; +import 'package:suji/core/extensions/string_extension.dart'; +import 'package:suji/core/utils/exception.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late DatabaseHelper mockDatabaseHelper; + late ShalatLocalDataSource shalatLocalDataSource; + + final tShalatTimeResponse = + ShalatTimeResponse.fromJson(json.decode(readJson('shalah_time.json'))); + final Shalat tShalat = tShalatTimeResponse.toEntity().first; + final tDateTime = DateTime.now(); + + setUpAll(() { + mockDatabaseHelper = MockDatabaseHelper(); + shalatLocalDataSource = + ShalatLocalDataSourceImpl(databaseHelper: mockDatabaseHelper); + }); + + group('Get Data Shalat by DateTime', () { + String tDateString = DateFormat('dd MMM y').format(tDateTime); + test('Should return data Shalat when call is successfull', () async { + // arrange + when(mockDatabaseHelper.getShalatTimeByDate(tDateString)) + .thenAnswer((_) async => tShalat); + + // act + final result = await shalatLocalDataSource.getShalatTimeByDate(tDateTime); + + // assert + verify(mockDatabaseHelper.getShalatTimeByDate(tDateString)); + + expect(result, tShalat); + }); + + test( + 'Should return null if the obj does not exist in [DatabaseHelper] when call is successfull', + () async { + // arrange + when(mockDatabaseHelper.getShalatTimeByDate(tDateString)) + .thenAnswer((_) async => null); + + // act + final result = await shalatLocalDataSource.getShalatTimeByDate(tDateTime); + + // assert + verify(mockDatabaseHelper.getShalatTimeByDate(tDateString)); + + expect(result, null); + }); + + test('Should throw [DatabaseException] when call is unsuccessfull', + () async { + // arrange + when(mockDatabaseHelper.getShalatTimeByDate(tDateString)) + .thenThrow(DatabaseException(AppString.databaseError)); + try { + // act + await shalatLocalDataSource.getShalatTimeByDate(tDateTime); + } on DatabaseException catch (e) { + // assert + verify(mockDatabaseHelper.getShalatTimeByDate(tDateString)); + + expect(e, isA()); + } + }); + }); + + group('Get Data Shalat by id', () { + const tPosition = Position( + longitude: 0, + latitude: 0, + timestamp: null, + accuracy: 0, + altitude: 0, + heading: 0, + speed: 0, + speedAccuracy: 0); + + final int tId = + '${DateFormat("dd MMM y").format(tDateTime)}${tPosition.latitude}${tPosition.longitude}' + .fastHash(); + + test('Should return data Shalat when call is successfull', () async { + // arrange + when(mockDatabaseHelper.getShalatTime(tId)) + .thenAnswer((_) async => tShalat); + + // act + final result = + await shalatLocalDataSource.getShalatTime(tDateTime, tPosition); + + // assert + verify(mockDatabaseHelper.getShalatTime(tId)); + + expect(result, tShalat); + }); + + test('Should return null if obj does not exist when call is successfull', + () async { + // arrange + when(mockDatabaseHelper.getShalatTime(tId)).thenAnswer((_) async => null); + + // act + final result = + await shalatLocalDataSource.getShalatTime(tDateTime, tPosition); + + // assert + verify(mockDatabaseHelper.getShalatTime(tId)); + + expect(result, null); + }); + + test('Should throw [DatabaseException] when call is unsuccessfull', + () async { + // arrange + when(mockDatabaseHelper.getShalatTime(tId)) + .thenThrow(DatabaseException(AppString.databaseError)); + try { + // act + await shalatLocalDataSource.getShalatTime(tDateTime, tPosition); + } catch (e) { + // assert + verify(mockDatabaseHelper.getShalatTime(tId)); + + expect(e, isA()); + } + }); + }); + + group('Insert Shalat', () { + test('Should return String when insert is successfull', () async { + // arrange + when(mockDatabaseHelper + .insertOrUpdateShalat(tShalatTimeResponse.toEntity())) + .thenAnswer((_) async => AppString.insertOrUpdateSuccess); + + //act + final result = await shalatLocalDataSource + .insertOrUpdateShalat(tShalatTimeResponse.toEntity()); + + // assert + verify(mockDatabaseHelper + .insertOrUpdateShalat(tShalatTimeResponse.toEntity())); + + expect(result, AppString.insertOrUpdateSuccess); + }); + + test('Should throw [DatabaseException] when insert is unsuccessfull', + () async { + // arrange + when(mockDatabaseHelper + .insertOrUpdateShalat(tShalatTimeResponse.toEntity())) + .thenThrow(DatabaseException(AppString.databaseError)); + + try { + //act + await shalatLocalDataSource + .insertOrUpdateShalat(tShalatTimeResponse.toEntity()); + } catch (e) { + // assert + verify(mockDatabaseHelper + .insertOrUpdateShalat(tShalatTimeResponse.toEntity())); + + expect(e, isA()); + } + }); + }); +} diff --git a/test/app/data/datasources/surah_local_data_source_test.dart b/test/app/data/datasources/surah_local_data_source_test.dart new file mode 100644 index 0000000..adb5ba5 --- /dev/null +++ b/test/app/data/datasources/surah_local_data_source_test.dart @@ -0,0 +1,253 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/list_surah_response.dart'; +import 'package:suji/app/data/model/surah_detail_response.dart'; +import 'package:suji/app/data/datasources/database_helper.dart'; +import 'package:suji/app/data/datasources/surah_local_data_source.dart'; +import 'package:suji/app/domain/entities/surah.dart'; +import 'package:suji/core/utils/exception.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late DatabaseHelper mockDatabaseHelper; + late SurahLocalDataSource surahLocalDataSource; + + setUpAll(() { + mockDatabaseHelper = MockDatabaseHelper(); + surahLocalDataSource = + SurahLocalDataSourceImpl(databaseHelper: mockDatabaseHelper); + }); + + final ListSurah tSurahResponse = + ListSurah.fromJson(json.decode(readJson('list_surah_response.json'))); + final List tSurahEntities = tSurahResponse.toEntity(); + + final tSurahDetailResponse = SurahDetailResponse.fromJson( + json.decode(readJson('surah_detail_response.json'))); + final tSurahDetailEntities = tSurahDetailResponse.toEntity(); + group('Get Surah', () { + test('Should return List when call is successful', () async { + // arrange + when(mockDatabaseHelper.getAllSurah()) + .thenAnswer((_) async => tSurahEntities); + + // act + final result = await surahLocalDataSource.getAllSurah(); + + // assert + verify(mockDatabaseHelper.getAllSurah()); + + expect(result, tSurahEntities); + }); + test('Should return list empty[] if no obj matches when call is successful', + () async { + // arrange + when(mockDatabaseHelper.getAllSurah()).thenAnswer((_) async => []); + + // act + final result = await surahLocalDataSource.getAllSurah(); + + // assert + verify(mockDatabaseHelper.getAllSurah()); + + expect(result, isEmpty); + }); + test('Should throw [DatabaseException] when call is unsuccessful', + () async { + // arrange + when(mockDatabaseHelper.getAllSurah()) + .thenThrow(DatabaseException(AppString.databaseError)); + try { + // act + await surahLocalDataSource.getAllSurah(); + } on DatabaseException catch (e) { + // assert + verify(mockDatabaseHelper.getAllSurah()); + + expect(e, isA()); + } + }); + }); + + group('Get Data Surah by Query', () { + test('Should return List that matches query when call is succesfull', + () async { + // arrange + when(mockDatabaseHelper + .getSurahByQuery(tSurahEntities.first.nameTransliterationId)) + .thenAnswer((_) async => [tSurahEntities.first]); + + // act + final result = await surahLocalDataSource + .getSurahByQuery(tSurahEntities.first.nameTransliterationId); + + // assert + verify(mockDatabaseHelper + .getSurahByQuery(tSurahEntities.first.nameTransliterationId)); + + expect(result, [tSurahEntities.first]); + }); + test( + 'Should return list Empty[] if no obj matches query when call is succesfull', + () async { + // arrange + when(mockDatabaseHelper + .getSurahByQuery(tSurahEntities.first.nameTransliterationId)) + .thenAnswer((_) async => []); + + // act + final result = await surahLocalDataSource + .getSurahByQuery(tSurahEntities.first.nameTransliterationId); + + // assert + verify(mockDatabaseHelper + .getSurahByQuery(tSurahEntities.first.nameTransliterationId)); + + expect(result, isEmpty); + }); + test('Should throw [DatabaseException] when call is unsuccesfull', + () async { + // arrange + when(mockDatabaseHelper + .getSurahByQuery(tSurahEntities.first.nameTransliterationId)) + .thenThrow(DatabaseException(AppString.databaseError)); + try { + // act + await surahLocalDataSource + .getSurahByQuery(tSurahEntities.first.nameTransliterationId); + } on DatabaseException catch (e) { + // assert + verify(mockDatabaseHelper + .getSurahByQuery(tSurahEntities.first.nameTransliterationId)); + + expect(e, isA()); + } + }); + }); + + group('Get Data Ayah by SurahNumber (SurahDetail)', () { + test('Should return List when call is successfull', () async { + // arrange + when(mockDatabaseHelper + .getAyahBySurahNumber(tSurahDetailEntities.first.number)) + .thenAnswer((_) async => tSurahDetailEntities); + + // act + final result = await surahLocalDataSource + .getAyahBySurahNumber(tSurahDetailEntities.first.number); + + // assert + verify(mockDatabaseHelper + .getAyahBySurahNumber(tSurahDetailEntities.first.number)); + + expect(result, tSurahDetailEntities); + }); + + test( + 'Should return list Empty[] if no obj matches when call is successfull', + () async { + // arrange + when(mockDatabaseHelper + .getAyahBySurahNumber(tSurahDetailEntities.first.number)) + .thenAnswer((_) async => []); + + // act + final result = await surahLocalDataSource + .getAyahBySurahNumber(tSurahDetailEntities.first.number); + + // assert + verify(mockDatabaseHelper + .getAyahBySurahNumber(tSurahDetailEntities.first.number)); + + expect(result, isEmpty); + }); + + test('Should throw [DatabaseException] when call is unsuccessfull', + () async { + // arrange + when(mockDatabaseHelper + .getAyahBySurahNumber(tSurahDetailEntities.first.number)) + .thenThrow(DatabaseException(AppString.databaseError)); + try { + // act + await surahLocalDataSource + .getAyahBySurahNumber(tSurahDetailEntities.first.number); + } on DatabaseException catch (e) { + // assert + verify(mockDatabaseHelper + .getAyahBySurahNumber(tSurahDetailEntities.first.number)); + + expect(e, isA()); + } + }); + }); + + group('Insert Surah', () { + test('Should return String when insert is successfull', () async { + // arrange + when(mockDatabaseHelper.insertOrUpdateSurah(tSurahEntities)) + .thenAnswer((_) async => AppString.insertOrUpdateSuccess); + + // act + final result = + await surahLocalDataSource.insertOrUpdateSurah(tSurahEntities); + + // assert + verify(mockDatabaseHelper.insertOrUpdateSurah(tSurahEntities)); + + expect(result, AppString.insertOrUpdateSuccess); + }); + test('Should throw [DatabaseException] when insert is unsuccessfull', + () async { + // arrange + when(mockDatabaseHelper.insertOrUpdateSurah(tSurahEntities)) + .thenThrow(DatabaseException(AppString.databaseError)); + try { + // act + await surahLocalDataSource.insertOrUpdateSurah(tSurahEntities); + } on DatabaseException catch (e) { + // assert + verify(mockDatabaseHelper.insertOrUpdateSurah(tSurahEntities)); + + expect(e, isA()); + } + }); + }); + group('Insert Ayah', () { + test('Should return String when insert is successfull', () async { + // arrange + when(mockDatabaseHelper.insertOrUpdateAyah(tSurahDetailEntities)) + .thenAnswer((_) async => AppString.insertOrUpdateSuccess); + + // act + final result = + await surahLocalDataSource.insertOrUpdateAyah(tSurahDetailEntities); + + // assert + verify(mockDatabaseHelper.insertOrUpdateAyah(tSurahDetailEntities)); + + expect(result, AppString.insertOrUpdateSuccess); + }); + test('Should throw [DatabaseException] when insert is unsuccessfull', + () async { + // arrange + when(mockDatabaseHelper.insertOrUpdateAyah(tSurahDetailEntities)) + .thenThrow(DatabaseException(AppString.databaseError)); + try { + // act + + await surahLocalDataSource.insertOrUpdateAyah(tSurahDetailEntities); + } on DatabaseException catch (e) { + // assert + verify(mockDatabaseHelper.insertOrUpdateAyah(tSurahDetailEntities)); + + expect(e, isA()); + } + }); + }); +} diff --git a/test/app/data/repository/shalat_repository_impl_test.dart b/test/app/data/repository/shalat_repository_impl_test.dart new file mode 100644 index 0000000..79f494d --- /dev/null +++ b/test/app/data/repository/shalat_repository_impl_test.dart @@ -0,0 +1,173 @@ +import 'dart:collection'; +import 'dart:convert'; + +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/shalat_time_response.dart'; +import 'package:suji/app/data/datasources/shalat_local_data_source.dart'; +import 'package:suji/app/data/datasources/shalat_remote_data_source.dart'; +import 'package:suji/app/data/repository/shalat_repository_impl.dart'; +import 'package:suji/app/domain/entities/shalat.dart'; +import 'package:suji/app/domain/repository/shalat_repository.dart'; +import 'package:suji/core/utils/exception.dart'; +import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +extension MultipleExpectations on PostExpectation { + void thenAnswerInOrder(List bodies) { + final answers = Queue.of(bodies); + thenAnswer((_) => answers.removeFirst()); + } +} + +void main() { + late ShalatLocalDataSource mockShalatLocalDataSource; + late ShalatRemoteDataSource mockShalatRemoteDataSource; + late Connectivity mockConnectivity; + late ShalatRepository shalatRepository; + + setUpAll(() { + WidgetsFlutterBinding.ensureInitialized(); + mockShalatLocalDataSource = MockShalatLocalDataSource(); + mockShalatRemoteDataSource = MockShalatRemoteDataSource(); + mockConnectivity = MockConnectivity(); + shalatRepository = ShalatRepositoryImpl( + shalatRemoteDataSource: mockShalatRemoteDataSource, + shalatLocalDataSource: mockShalatLocalDataSource, + connectivity: mockConnectivity); + }); + + group('Get Shalat Time', () { + const tPosition = Position( + longitude: 0, + latitude: 0, + timestamp: null, + accuracy: 0, + altitude: 0, + heading: 0, + speed: 0, + speedAccuracy: 0); + final tDateTime = DateTime.now(); + final tShalatTimeResponse = + ShalatTimeResponse.fromJson(json.decode(readJson('shalah_time.json'))); + final Shalat tShalatEntities = tShalatTimeResponse.toEntity().first; + + test('should get data shalat when call to local data source is successfull', + () async { + // arrange + when(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)) + .thenAnswer((_) async => tShalatEntities); + + // act + final result = await shalatRepository.getShalatTime(tDateTime, tPosition); + + // assert + verify(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)); + + final actual = result.getOrElse(() => Shalat()); + expect(actual, tShalatEntities); + }); + + test( + 'should return DatabaseFailure when call to local data source is unsuccessfull', + () async { + // arrange + when(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)) + .thenThrow(DatabaseException(AppString.databaseError)); + + // act + final result = await shalatRepository.getShalatTime(tDateTime, tPosition); + + // assert + verify(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)); + expect( + result, equals(const Left(DatabaseFailure(AppString.databaseError)))); + }); + + test( + 'should get data shalat when call to remote data source is successfull', + () async { + // arrange + when(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)) + .thenAnswerInOrder( + [Future(() => null), Future(() => tShalatEntities)]); + when(mockShalatRemoteDataSource.getShalatTime(tDateTime, tPosition)) + .thenAnswer((_) async => tShalatTimeResponse); + when(mockShalatLocalDataSource + .insertOrUpdateShalat(tShalatTimeResponse.toEntity())) + .thenAnswer((_) async => ''); + + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.wifi); + + // act + final result = await shalatRepository.getShalatTime(tDateTime, tPosition); + + // assert + verify(mockShalatLocalDataSource + .insertOrUpdateShalat(tShalatTimeResponse.toEntity())); + verify(mockConnectivity.checkConnectivity()); + verify(mockShalatRemoteDataSource.getShalatTime(tDateTime, tPosition)); + verify(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)) + .called(2); + + final actual = result.getOrElse(() => Shalat()); + expect(actual, tShalatEntities); + }); + + test( + 'should return ServerFailure when call to remota data source is unsuccessfull', + () async { + // arrange + when(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)) + .thenAnswer((_) async => null); + when(mockShalatRemoteDataSource.getShalatTime(tDateTime, tPosition)) + .thenThrow(ServerException(AppString.socketException)); + + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.wifi); + + // act + final result = await shalatRepository.getShalatTime(tDateTime, tPosition); + + // assert + verify(mockConnectivity.checkConnectivity()); + verify(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)) + .called(1); + verify(mockShalatRemoteDataSource.getShalatTime(tDateTime, tPosition)) + .called(1); + + expect( + result, equals(const Left(ServerFailure(AppString.socketException)))); + }); + + test( + 'should return ConnectionFailure when device not connected to internet', + () async { + // arrange + when(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)) + .thenAnswer((_) async => null); + + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.none); + + // act + final result = await shalatRepository.getShalatTime(tDateTime, tPosition); + + // assert + verify(mockConnectivity.checkConnectivity()); + verify(mockShalatLocalDataSource.getShalatTime(tDateTime, tPosition)) + .called(1); + + expect(result, + equals(const Left(ConnectionFailure(AppString.socketException)))); + }); + }); +} diff --git a/test/app/data/repository/surah_repository_impl_test.dart b/test/app/data/repository/surah_repository_impl_test.dart new file mode 100644 index 0000000..a420182 --- /dev/null +++ b/test/app/data/repository/surah_repository_impl_test.dart @@ -0,0 +1,349 @@ +import 'dart:convert'; + +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/asmaul_husna_response.dart'; +import 'package:suji/app/data/model/daily_pray_response.dart'; +import 'package:suji/app/data/model/list_surah_response.dart'; +import 'package:suji/app/data/datasources/surah_local_data_source.dart'; +import 'package:suji/app/data/datasources/surah_remote_data_source.dart'; +import 'package:suji/app/data/model/surah_detail_response.dart'; +import 'package:suji/app/data/repository/surah_repository_impl.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/core/utils/exception.dart'; +import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late SurahRemoteDataSource mockSurahRemoteDataSource; + late SurahLocalDataSource mockSurahLocalDataSource; + late Connectivity mockConnectivity; + late SurahRepository surahRepository; + final ListSurah tSurahResponse = + ListSurah.fromJson(json.decode(readJson('list_surah_response.json'))); + final tSurahEntities = tSurahResponse.toEntity(); + + setUpAll(() { + WidgetsFlutterBinding.ensureInitialized(); + mockConnectivity = MockConnectivity(); + mockSurahLocalDataSource = MockSurahLocalDataSource(); + mockSurahRemoteDataSource = MockSurahRemoteDataSource(); + surahRepository = SurahRepositoryImpl( + connectivity: mockConnectivity, + surahRemoteDataSource: mockSurahRemoteDataSource, + surahLocalDataSource: mockSurahLocalDataSource); + }); + + group('Get Daily Pray', () { + final tDailyPray = DataDailyPrayResponse.fromJson( + json.decode(readJson('daily_pray.json'))); + final tDailyPrayEntities = tDailyPray.toEntity(); + + test('Should return List when call from local is successfull', + () async { + // arrange + when(mockSurahLocalDataSource.getDailyPray()) + .thenAnswer((_) async => tDailyPray); + + // act + final result = await surahRepository.getDailyPray(); + + // assert + verify(mockSurahLocalDataSource.getDailyPray()); + + final actual = result.getOrElse(() => []); + expect(actual, tDailyPrayEntities); + }); + + test('Should return DatabaseFailure when call from local is unsuccessfull', + () async { + // arrange + when(mockSurahLocalDataSource.getDailyPray()) + .thenThrow(DatabaseException(AppString.databaseError)); + + // act + final result = await surahRepository.getDailyPray(); + + // assert + verify(mockSurahLocalDataSource.getDailyPray()); + + expect( + result, equals(const Left(DatabaseFailure(AppString.databaseError)))); + }); + }); + + group('Get Asmaul Husna', () { + final tAsmaulHusnaResponse = AsmaulHusnaResponse.fromJson( + json.decode(readJson('asmaul_husna.json'))); + final tAsmaulHusnaEntities = tAsmaulHusnaResponse.toEntity(); + + test('Should return List when call from local is successfull', + () async { + // arrange + when(mockSurahLocalDataSource.getAsmaulHusna()) + .thenAnswer((_) async => tAsmaulHusnaResponse); + + // act + final result = await surahRepository.getAsmaulHusna(); + + // assert + verify(mockSurahLocalDataSource.getAsmaulHusna()); + + final actual = result.getOrElse(() => []); + expect(actual, tAsmaulHusnaEntities); + }); + + test('Should return DatabaseFailure when call from local is unsuccessfull', + () async { + // arrange + when(mockSurahLocalDataSource.getAsmaulHusna()) + .thenThrow(DatabaseException(AppString.databaseError)); + + // act + final result = await surahRepository.getAsmaulHusna(); + + // assert + verify(mockSurahLocalDataSource.getAsmaulHusna()); + + expect( + result, equals(const Left(DatabaseFailure(AppString.databaseError)))); + }); + }); + + group('Get Surah by Query', () { + final query = tSurahEntities.first.nameTransliterationId; + test( + 'Should return data surah if query matches when call from local is successfull', + () async { + // arrange + when(mockSurahLocalDataSource.getSurahByQuery(query)) + .thenAnswer((_) async => tSurahEntities); + // act + final result = await surahRepository.getSurahByQuery(query); + + // assert + verify(mockSurahLocalDataSource.getSurahByQuery(query)); + + expect(result, equals(right(tSurahEntities))); + }); + + test('Should return DatabaseFailure when call from local is unsuccessfull', + () async { + // arrange + when(mockSurahLocalDataSource.getSurahByQuery(query)) + .thenThrow(DatabaseException(AppString.databaseError)); + + // act + final result = await surahRepository.getSurahByQuery(query); + + // assert + verify(mockSurahLocalDataSource.getSurahByQuery(query)); + + expect( + result, equals(const Left(DatabaseFailure(AppString.databaseError)))); + }); + }); + + group('Get Ayah by SurahNumber', () { + final SurahDetailResponse tSurahDetailResponse = + SurahDetailResponse.fromJson( + json.decode(readJson('surah_detail_response.json'))); + final tSurahDetailEntities = tSurahDetailResponse.toEntity(); + final surahNumber = tSurahDetailEntities.first.number; + test('Should return data ayah when call from local is successfull', + () async { + // arrange + when(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)) + .thenAnswer((_) async => tSurahDetailEntities); + // act + final result = await surahRepository.getAyahBySurahNumber(surahNumber); + + // assert + verify(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)); + + final actual = result.getOrElse(() => []); + expect(actual, tSurahDetailEntities); + }); + + test('Should return [DatabaseFailure] when call from local is unsuccessful', + () async { + // arrange + when(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)) + .thenThrow(DatabaseException(AppString.databaseError)); + // act + final result = await surahRepository.getAyahBySurahNumber(surahNumber); + + // assert + verify(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)); + + expect( + result, equals(const Left(DatabaseFailure(AppString.databaseError)))); + }); + + test('Should return data ayah when call from remote is successfull', + () async { + // arrange + when(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)) + .thenAnswer((_) async => []); + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.wifi); + when(mockSurahRemoteDataSource.getAyahBySurahNumber(surahNumber)) + .thenAnswer((_) async => tSurahDetailResponse); + when(mockSurahLocalDataSource.insertOrUpdateAyah(tSurahDetailEntities)) + .thenAnswer((_) async => ''); + + // act + final result = await surahRepository.getAyahBySurahNumber(surahNumber); + + // assert + verify(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)); + verify(mockConnectivity.checkConnectivity()); + verify(mockSurahRemoteDataSource.getAyahBySurahNumber(surahNumber)); + verify(mockSurahLocalDataSource.insertOrUpdateAyah(tSurahDetailEntities)); + + final actual = result.getOrElse(() => []); + expect(actual, tSurahDetailEntities); + }); + + test('should return [ServerFailure] when call from remote is unsuccessfull', + () async { + // arrange + when(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)) + .thenAnswer((_) async => []); + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.wifi); + when(mockSurahRemoteDataSource.getAyahBySurahNumber(surahNumber)) + .thenThrow(ServerException(AppString.socketException)); + + // act + final result = await surahRepository.getAyahBySurahNumber(surahNumber); + + // assert + verify(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)); + verify(mockConnectivity.checkConnectivity()); + verify(mockSurahRemoteDataSource.getAyahBySurahNumber(surahNumber)); + expect( + result, equals(const Left(ServerFailure(AppString.socketException)))); + }); + + test( + 'should return [ConnectionFailure] when cache is empty and device is not connected', + () async { + // arrange + when(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)) + .thenAnswer((_) async => []); + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.none); + + // act + final result = await surahRepository.getAyahBySurahNumber(surahNumber); + + // assert + verify(mockSurahLocalDataSource.getAyahBySurahNumber(surahNumber)); + verify(mockConnectivity.checkConnectivity()); + + expect(result, + equals(const Left(ConnectionFailure(AppString.socketException)))); + }); + }); + group('Get Data Surah', () { + test('Should return data surah when call from local is successfull', + () async { + // arrange + when(mockSurahLocalDataSource.getAllSurah()) + .thenAnswer((_) async => tSurahEntities); + // act + final result = await surahRepository.getAllSurah(); + + // assert + verify(mockSurahLocalDataSource.getAllSurah()); + + final actual = result.getOrElse(() => []); + expect(actual, tSurahEntities); + }); + + test('Should return DatabaseFailure when call from local is unsuccessful', + () async { + // arrange + when(mockSurahLocalDataSource.getAllSurah()) + .thenThrow(DatabaseException(AppString.databaseError)); + // act + final result = await surahRepository.getAllSurah(); + + // assert + verify(mockSurahLocalDataSource.getAllSurah()); + + expect( + result, equals(const Left(DatabaseFailure(AppString.databaseError)))); + }); + + test('Should return data surah when call from remote is successfull', + () async { + // arrange + when(mockSurahLocalDataSource.getAllSurah()).thenAnswer((_) async => []); + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.wifi); + when(mockSurahRemoteDataSource.getAllSurah()) + .thenAnswer((_) async => tSurahResponse); + when(mockSurahLocalDataSource.insertOrUpdateSurah(tSurahEntities)) + .thenAnswer((_) async => ''); + + // act + final result = await surahRepository.getAllSurah(); + + // assert + verify(mockSurahLocalDataSource.getAllSurah()); + verify(mockConnectivity.checkConnectivity()); + verify(mockSurahRemoteDataSource.getAllSurah()); + verify(mockSurahLocalDataSource.insertOrUpdateSurah(tSurahEntities)); + + final actual = result.getOrElse(() => []); + expect(actual, tSurahEntities); + }); + + test('Should return ServerFailure when call from remote is unsuccessfull', + () async { + // arrange + when(mockSurahLocalDataSource.getAllSurah()).thenAnswer((_) async => []); + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.wifi); + when(mockSurahRemoteDataSource.getAllSurah()) + .thenThrow(ServerException(AppString.socketException)); + + // act + final result = await surahRepository.getAllSurah(); + + // assert + verify(mockSurahLocalDataSource.getAllSurah()); + verify(mockConnectivity.checkConnectivity()); + verify(mockSurahRemoteDataSource.getAllSurah()); + expect( + result, equals(const Left(ServerFailure(AppString.socketException)))); + }); + + test( + 'Should return ConnectionFailure when cache is empty and device is not connected', + () async { + // arrange + when(mockSurahLocalDataSource.getAllSurah()).thenAnswer((_) async => []); + when(mockConnectivity.checkConnectivity()) + .thenAnswer((_) async => ConnectivityResult.none); + + // act + final result = await surahRepository.getAllSurah(); + + // assert + verify(mockSurahLocalDataSource.getAllSurah()); + verify(mockConnectivity.checkConnectivity()); + + expect(result, + equals(const Left(ConnectionFailure(AppString.socketException)))); + }); + }); +} diff --git a/test/app/domain/usecases/get_all_surah_usecase_test.dart b/test/app/domain/usecases/get_all_surah_usecase_test.dart new file mode 100644 index 0000000..cadb5f8 --- /dev/null +++ b/test/app/domain/usecases/get_all_surah_usecase_test.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/list_surah_response.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_all_surah_usecase.dart'; +import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late SurahRepository mockSurahRepository; + late GetAllSurahUsecase getAllSurahUsecase; + + setUp(() { + mockSurahRepository = MockSurahRepository(); + getAllSurahUsecase = + GetAllSurahUsecase(surahRepository: mockSurahRepository); + }); + + group('Get All Surah Test', () { + final ListSurah tSurahResponse = + ListSurah.fromJson(json.decode(readJson('list_surah_response.json'))); + final tSurahEntities = tSurahResponse.toEntity(); + test('Should return all surah when call is successful', () async { + // arrange + when(mockSurahRepository.getAllSurah()) + .thenAnswer((_) async => right(tSurahEntities)); + + //act + final result = await getAllSurahUsecase.invoke(); + + // assert + verify(mockSurahRepository.getAllSurah()); + + expect(result, equals(right(tSurahEntities))); + }); + test('Should return [DatabaseFailure] when call from local is unsuccessful', + () async { + // arrange + when(mockSurahRepository.getAllSurah()).thenAnswer( + (_) async => left(const DatabaseFailure(AppString.databaseError))); + + //act + final result = await getAllSurahUsecase.invoke(); + + // assert + verify(mockSurahRepository.getAllSurah()); + + expect( + result, equals(left(const DatabaseFailure(AppString.databaseError)))); + }); + test('Should return [ServerFailure] when call from remote is unsuccessful', + () async { + // arrange + when(mockSurahRepository.getAllSurah()).thenAnswer( + (_) async => left(const ServerFailure(AppString.socketException))); + + //act + final result = await getAllSurahUsecase.invoke(); + + // assert + verify(mockSurahRepository.getAllSurah()); + + expect( + result, equals(left(const ServerFailure(AppString.socketException)))); + }); + test( + 'Should return [ConnectionFailure] when device not connected to internet', + () async { + // arrange + when(mockSurahRepository.getAllSurah()).thenAnswer((_) async => + left(const ConnectionFailure(AppString.socketException))); + + //act + final result = await getAllSurahUsecase.invoke(); + + // assert + verify(mockSurahRepository.getAllSurah()); + + expect(result, + equals(left(const ConnectionFailure(AppString.socketException)))); + }); + }); +} diff --git a/test/app/domain/usecases/get_asmaul_husna_usecase_test.dart b/test/app/domain/usecases/get_asmaul_husna_usecase_test.dart new file mode 100644 index 0000000..25b8fbd --- /dev/null +++ b/test/app/domain/usecases/get_asmaul_husna_usecase_test.dart @@ -0,0 +1,58 @@ +import 'dart:convert'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/asmaul_husna_response.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_asmaul_husna_usecase.dart'; +import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late SurahRepository mockSurahRepository; + late GetAsmaulHusnaUsecase getAsmaulHusnaUsecase; + + setUp(() { + mockSurahRepository = MockSurahRepository(); + getAsmaulHusnaUsecase = + GetAsmaulHusnaUsecase(surahRepository: mockSurahRepository); + }); + + group('Get Asmaul Husna Test', () { + final tAsmaulHusnaResponse = AsmaulHusnaResponse.fromJson( + json.decode(readJson('asmaul_husna.json'))); + final tAsmaulHusnaEntities = tAsmaulHusnaResponse.toEntity(); + test('Should return all surah when call is successful', () async { + // arrange + when(mockSurahRepository.getAsmaulHusna()) + .thenAnswer((_) async => right(tAsmaulHusnaEntities)); + + //act + final result = await getAsmaulHusnaUsecase.invoke(); + + // assert + verify(mockSurahRepository.getAsmaulHusna()); + + expect(result, equals(right(tAsmaulHusnaEntities))); + }); + test('Should return [DatabaseFailure] when call from local is unsuccessful', + () async { + // arrange + when(mockSurahRepository.getAsmaulHusna()).thenAnswer( + (_) async => left(const DatabaseFailure(AppString.databaseError))); + + //act + final result = await getAsmaulHusnaUsecase.invoke(); + + // assert + verify(mockSurahRepository.getAsmaulHusna()); + + expect( + result, equals(left(const DatabaseFailure(AppString.databaseError)))); + }); + }); +} diff --git a/test/app/domain/usecases/get_ayah_by_surah_number_usecase_test.dart b/test/app/domain/usecases/get_ayah_by_surah_number_usecase_test.dart new file mode 100644 index 0000000..ba1774b --- /dev/null +++ b/test/app/domain/usecases/get_ayah_by_surah_number_usecase_test.dart @@ -0,0 +1,91 @@ +import 'dart:convert'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/surah_detail_response.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_ayah_by_surah_number_usecase.dart'; +import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late SurahRepository mockSurahRepository; + late GetAyahBySurahNumberUsecase getAyahBySurahNumberUsecase; + + setUp(() { + mockSurahRepository = MockSurahRepository(); + getAyahBySurahNumberUsecase = + GetAyahBySurahNumberUsecase(surahRepository: mockSurahRepository); + }); + + group('Get Ayah by SurahNumber Test', () { + final SurahDetailResponse tSurahDetailResponse = + SurahDetailResponse.fromJson( + json.decode(readJson('surah_detail_response.json'))); + final tSurahDetailEntities = tSurahDetailResponse.toEntity(); + final surahNumber = tSurahDetailEntities.first.number; + test('Should return all ayah when call is successful', () async { + // arrange + when(mockSurahRepository.getAyahBySurahNumber(surahNumber)) + .thenAnswer((_) async => right(tSurahDetailEntities)); + + //act + final result = await getAyahBySurahNumberUsecase.invoke(surahNumber); + + // assert + verify(mockSurahRepository.getAyahBySurahNumber(surahNumber)); + + expect(result, equals(right(tSurahDetailEntities))); + }); + test('Should return [DatabaseFailure] when call from local is unsuccessful', + () async { + // arrange + when(mockSurahRepository.getAyahBySurahNumber(surahNumber)).thenAnswer( + (_) async => left(const DatabaseFailure(AppString.databaseError))); + + //act + final result = await getAyahBySurahNumberUsecase.invoke(surahNumber); + + // assert + verify(mockSurahRepository.getAyahBySurahNumber(surahNumber)); + + expect( + result, equals(left(const DatabaseFailure(AppString.databaseError)))); + }); + test('Should return [ServerFailure] when call from remote is unsuccessful', + () async { + // arrange + when(mockSurahRepository.getAyahBySurahNumber(surahNumber)).thenAnswer( + (_) async => left(const ServerFailure(AppString.databaseError))); + + //act + final result = await getAyahBySurahNumberUsecase.invoke(surahNumber); + + // assert + verify(mockSurahRepository.getAyahBySurahNumber(surahNumber)); + + expect( + result, equals(left(const ServerFailure(AppString.databaseError)))); + }); + test( + 'Should return [ConnectionFailure] when call from remote is unsuccessful', + () async { + // arrange + when(mockSurahRepository.getAyahBySurahNumber(surahNumber)).thenAnswer( + (_) async => left(const ConnectionFailure(AppString.databaseError))); + + //act + final result = await getAyahBySurahNumberUsecase.invoke(surahNumber); + + // assert + verify(mockSurahRepository.getAyahBySurahNumber(surahNumber)); + + expect(result, + equals(left(const ConnectionFailure(AppString.databaseError)))); + }); + }); +} diff --git a/test/app/domain/usecases/get_daily_pray_usecase_test.dart b/test/app/domain/usecases/get_daily_pray_usecase_test.dart new file mode 100644 index 0000000..27727fb --- /dev/null +++ b/test/app/domain/usecases/get_daily_pray_usecase_test.dart @@ -0,0 +1,58 @@ +import 'dart:convert'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/daily_pray_response.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_daily_pray_usecase.dart'; +import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late SurahRepository mockSurahRepository; + late GetDailyPrayUsecase getDailyPrayUsecase; + + setUp(() { + mockSurahRepository = MockSurahRepository(); + getDailyPrayUsecase = + GetDailyPrayUsecase(surahRepository: mockSurahRepository); + }); + + group('Get Daily Pray Test', () { + final tDailyPray = DataDailyPrayResponse.fromJson( + json.decode(readJson('daily_pray.json'))); + final tDailyPrayEntities = tDailyPray.toEntity(); + test('Should return all daily pray when call is successful', () async { + // arrange + when(mockSurahRepository.getDailyPray()) + .thenAnswer((_) async => right(tDailyPrayEntities)); + + //act + final result = await getDailyPrayUsecase.invoke(); + + // assert + verify(mockSurahRepository.getDailyPray()); + + expect(result, equals(right(tDailyPrayEntities))); + }); + test('Should return [DatabaseFailure] when call from local is unsuccessful', + () async { + // arrange + when(mockSurahRepository.getDailyPray()).thenAnswer( + (_) async => left(const DatabaseFailure(AppString.databaseError))); + + //act + final result = await getDailyPrayUsecase.invoke(); + + // assert + verify(mockSurahRepository.getDailyPray()); + + expect( + result, equals(left(const DatabaseFailure(AppString.databaseError)))); + }); + }); +} diff --git a/test/app/domain/usecases/get_shalat_time_usecase_test.dart b/test/app/domain/usecases/get_shalat_time_usecase_test.dart new file mode 100644 index 0000000..89d63b9 --- /dev/null +++ b/test/app/domain/usecases/get_shalat_time_usecase_test.dart @@ -0,0 +1,103 @@ +import 'dart:convert'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/shalat_time_response.dart'; +import 'package:suji/app/domain/entities/shalat.dart'; +import 'package:suji/app/domain/repository/shalat_repository.dart'; +import 'package:suji/app/domain/usescases/get_shalat_time_usecase.dart'; +import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late ShalatRepository mockShalatRepository; + late GetShalatTimeUsecase getShalatTimeUsecase; + + setUp(() { + mockShalatRepository = MockShalatRepository(); + getShalatTimeUsecase = + GetShalatTimeUsecase(shalatRepository: mockShalatRepository); + }); + + group('Get Shalat Time Test', () { + const tPosition = Position( + longitude: 0, + latitude: 0, + timestamp: null, + accuracy: 0, + altitude: 0, + heading: 0, + speed: 0, + speedAccuracy: 0); + final tDateTime = DateTime.now(); + final tShalatTimeResponse = + ShalatTimeResponse.fromJson(json.decode(readJson('shalah_time.json'))); + final Shalat tShalatEntities = tShalatTimeResponse.toEntity().first; + + test('Should return data shalat when call is successful', () async { + // arrange + when(mockShalatRepository.getShalatTime(tDateTime, tPosition)) + .thenAnswer((_) async => right(tShalatEntities)); + + //act + final result = await getShalatTimeUsecase.invoke(tDateTime, tPosition); + + // assert + verify(mockShalatRepository.getShalatTime(tDateTime, tPosition)); + + expect(result, equals(right(tShalatEntities))); + }); + test('Should return [DatabaseFailure] when call from local is unsuccessful', + () async { + // arrange + when(mockShalatRepository.getShalatTime(tDateTime, tPosition)).thenAnswer( + (_) async => left(const DatabaseFailure(AppString.databaseError))); + + //act + final result = await getShalatTimeUsecase.invoke(tDateTime, tPosition); + + // assert + verify(mockShalatRepository.getShalatTime(tDateTime, tPosition)); + + expect( + result, equals(left(const DatabaseFailure(AppString.databaseError)))); + }); + test('Should return [ServerFailure] when call from remote is unsuccessful', + () async { + // arrange + when(mockShalatRepository.getShalatTime(tDateTime, tPosition)).thenAnswer( + (_) async => left(const ServerFailure(AppString.socketException))); + + //act + final result = await getShalatTimeUsecase.invoke(tDateTime, tPosition); + + // assert + verify(mockShalatRepository.getShalatTime(tDateTime, tPosition)); + + expect( + result, equals(left(const ServerFailure(AppString.socketException)))); + }); + test( + 'Should return [ConnectionFailure] when device not connected to internet', + () async { + // arrange + when(mockShalatRepository.getShalatTime(tDateTime, tPosition)).thenAnswer( + (_) async => + left(const ConnectionFailure(AppString.socketException))); + + //act + final result = await getShalatTimeUsecase.invoke(tDateTime, tPosition); + + // assert + verify(mockShalatRepository.getShalatTime(tDateTime, tPosition)); + + expect(result, + equals(left(const ConnectionFailure(AppString.socketException)))); + }); + }); +} diff --git a/test/app/domain/usecases/get_surah_by_query_usecase_test.dart b/test/app/domain/usecases/get_surah_by_query_usecase_test.dart new file mode 100644 index 0000000..902c76d --- /dev/null +++ b/test/app/domain/usecases/get_surah_by_query_usecase_test.dart @@ -0,0 +1,60 @@ +import 'dart:convert'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/list_surah_response.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_surah_by_query_usecase.dart'; +import 'package:suji/core/utils/failure.dart'; +import 'package:suji/core/values/constant.dart'; + +import '../../../helper/test_helper.mocks.dart'; +import '../../../json_reader.dart'; + +void main() { + late SurahRepository mockSurahRepository; + late GetSurahByQueryUsecase getSurahByQueryUsecase; + + setUp(() { + mockSurahRepository = MockSurahRepository(); + getSurahByQueryUsecase = + GetSurahByQueryUsecase(surahRepository: mockSurahRepository); + }); + + group('Get Surah by Query Test', () { + final ListSurah tSurahResponse = + ListSurah.fromJson(json.decode(readJson('list_surah_response.json'))); + final tSurahEntities = tSurahResponse.toEntity(); + final query = tSurahEntities.first.nameTransliterationId; + test('Should return all surah which matches query when call is successful', + () async { + // arrange + when(mockSurahRepository.getSurahByQuery(query)) + .thenAnswer((_) async => right(tSurahEntities)); + + //act + final result = await getSurahByQueryUsecase.invoke(query); + + // assert + verify(mockSurahRepository.getSurahByQuery(query)); + + expect(result, equals(right(tSurahEntities))); + }); + test('Should return [DatabaseFailure] when call from local is unsuccessful', + () async { + // arrange + when(mockSurahRepository.getSurahByQuery(query)).thenAnswer( + (_) async => left(const DatabaseFailure(AppString.databaseError))); + + //act + final result = await getSurahByQueryUsecase.invoke(query); + + // assert + verify(mockSurahRepository.getSurahByQuery(query)); + + expect( + result, equals(left(const DatabaseFailure(AppString.databaseError)))); + }); + }); +} diff --git a/test/app/modules/asmaul_husna/controllers/asmaul_husna_controller_test.dart b/test/app/modules/asmaul_husna/controllers/asmaul_husna_controller_test.dart new file mode 100644 index 0000000..4f20b90 --- /dev/null +++ b/test/app/modules/asmaul_husna/controllers/asmaul_husna_controller_test.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/data/model/asmaul_husna_response.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_asmaul_husna_usecase.dart'; +import 'package:suji/app/modules/asmaul_husna/controllers/asmaul_husna_controller.dart'; + +import '../../../../helper/test_helper.mocks.dart'; +import '../../../../json_reader.dart'; + +void main() { + late GetAsmaulHusnaUsecase mockGetAsmaulHusnaUsecase; + late AsmaulHusnaController asmaulHusnaController; + final tAsmaulHusnaResponse = + AsmaulHusnaResponse.fromJson(json.decode(readJson('asmaul_husna.json'))); + final tAsmaulHusnaEntities = tAsmaulHusnaResponse.toEntity(); + setUpAll(() { + mockGetAsmaulHusnaUsecase = MockGetAsmaulHusnaUsecase(); + asmaulHusnaController = + AsmaulHusnaController(getAsmaulHusnaUsecase: mockGetAsmaulHusnaUsecase); + }); + + test('initialize Asmaul Husna Controller', () async { + // arrange + when(mockGetAsmaulHusnaUsecase.invoke()) + .thenAnswer((_) async => Right(tAsmaulHusnaEntities)); + + // act + Get.put(asmaulHusnaController); + + // verify + verify(mockGetAsmaulHusnaUsecase.invoke()); + + expect(asmaulHusnaController.status.isLoading, true); + Get.delete(); + }); +} diff --git a/test/app/modules/asmaul_husna/views/asmaul_husna_view_test.dart b/test/app/modules/asmaul_husna/views/asmaul_husna_view_test.dart new file mode 100644 index 0000000..a690496 --- /dev/null +++ b/test/app/modules/asmaul_husna/views/asmaul_husna_view_test.dart @@ -0,0 +1,43 @@ +// import 'dart:convert'; + +// import 'package:dartz/dartz.dart'; +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:get/get.dart'; +// import 'package:mockito/mockito.dart'; +// import 'package:suji/app/data/model/asmaul_husna_response.dart'; +// import 'package:suji/app/domain/usescases/get_asmaul_husna_usecase.dart'; +// import 'package:suji/app/modules/asmaul_husna/controllers/asmaul_husna_controller.dart'; +// import 'package:suji/app/modules/asmaul_husna/views/asmaul_husna_view.dart'; + +// import '../../../../helper/test_helper.mocks.dart'; +// import '../../../../json_reader.dart'; + +// void main() { +// // late AsmaulHusnaController asmaulHusnaController; +// late GetAsmaulHusnaUsecase mockGetAsmaulHusnaUsecase; +// final tAsmaulHusnaResponse = AsmaulHusnaResponse.fromJson( +// json.decode(readJson('asmaul_husna.json'))); +// final tAsmaulHusnaEntities = tAsmaulHusnaResponse.toEntity(); +// const asmaulHusnaView = GetMaterialApp( +// home: AsmaulHusnaView(), +// ); + +// setUp(() { +// Get.testMode = true; +// mockGetAsmaulHusnaUsecase = MockGetAsmaulHusnaUsecase(); +// Get.put(AsmaulHusnaController(getAsmaulHusnaUsecase: mockGetAsmaulHusnaUsecase)); +// }); +// tearDown(() { +// // Get.delete(); +// Get.reset(); +// }); + +// testWidgets('initial state of asmaulHusnaView', (widgetTester) async { +// // arrange +// when(mockGetAsmaulHusnaUsecase.invoke()).thenAnswer((_)async=> right(tAsmaulHusnaEntities)); + +// await widgetTester.pumpWidget(asmaulHusnaView); +// final asmaulHusnaText = find.text(tAsmaulHusnaEntities.first.latin); +// expect(asmaulHusnaText, findsOneWidget); +// }); +// } diff --git a/test/app/modules/tasbih/controllers/tasbih_controller_test.dart b/test/app/modules/tasbih/controllers/tasbih_controller_test.dart new file mode 100644 index 0000000..1dbb936 --- /dev/null +++ b/test/app/modules/tasbih/controllers/tasbih_controller_test.dart @@ -0,0 +1,61 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:suji/app/modules/tasbih/controllers/tasbih_controller.dart'; + +void main() { + late TasbihController tasbihController; + + setUp(() { + tasbihController = TasbihController(); + }); + tearDown(() => Get.reset()); + group('Tasbih Controller', () { + const int initialCounter = 0; + const int initialTarget = 11; + test('initial test Tasbih Controller', () { + expect(tasbihController.counter, initialCounter); + expect(tasbihController.target, initialTarget); + expect(tasbihController.textEditingController.text, isEmpty); + + Get.put(tasbihController); + expect(tasbihController.textEditingController.text, isNotEmpty); + + Get.delete(); + }); + test('Should increase counter when counter is lower than target', () { + Get.put(tasbihController); + for (int i = 0; i <= initialTarget; i++) { + expect(tasbihController.counter, i); + tasbihController.counter++; + } + expect(tasbihController.counter, initialTarget); + Get.delete(); + }); + test( + 'Should return 0 if value of counter lower than zero when decrease counter tap', + () { + Get.put(tasbihController); + tasbihController.counter--; + expect(tasbihController.counter, 0); + Get.delete(); + }); + + test('Should change the value of target', () { + Get.put(tasbihController); + tasbihController.target = 33; + expect(tasbihController.target, 33); + expect(tasbihController.textEditingController.text, '33'); + Get.delete(); + }); + + test( + 'Should change the value of target when call onChanged from [TextFormField]', + () { + Get.put(tasbihController); + tasbihController.onChangedTextField(''); + expect(tasbihController.target, 0); + expect(tasbihController.textEditingController.text, '0'); + Get.delete(); + }); + }); +} diff --git a/test/app/modules/tasbih/views/tasbih_view_test.dart b/test/app/modules/tasbih/views/tasbih_view_test.dart new file mode 100644 index 0000000..2347d01 --- /dev/null +++ b/test/app/modules/tasbih/views/tasbih_view_test.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:mockito/mockito.dart'; +import 'package:suji/app/modules/tasbih/controllers/tasbih_controller.dart'; +import 'package:suji/app/modules/tasbih/views/tasbih_view.dart'; + +class TasbihControllerMock extends GetxController + with Mock + implements TasbihController { + @override + TextEditingController get textEditingController => TextEditingController(); +} + +void main() { + late TasbihController tasbihController; + const tasbihView = GetMaterialApp( + home: TasbihView(), + ); + + setUp(() { + tasbihController = Get.put(TasbihController()); + }); + tearDown(() { + Get.delete(); + }); + testWidgets('initial state of TasbihView', (widgetTester) async { + await widgetTester.pumpWidget(tasbihView); + final counter = find.text('0'); + expect(counter, findsOneWidget); + }); + testWidgets('Should increase counter', (widgetTester) async { + // act + await widgetTester.pumpWidget(tasbihView); + final counterButton = find.byKey(const Key('IncreaseButton')); + await widgetTester.tap(counterButton); + await widgetTester.pump(); + final counter = find.text('1'); + + // assert + expect(counterButton, findsOneWidget); + expect(counter, findsOneWidget); + }); + + testWidgets('Should decrease counter', (widgetTester) async { + // act + await widgetTester.pumpWidget(tasbihView); + final counterButton = find.byKey(const Key('DecreaseButton')); + await widgetTester.tap(counterButton); + await widgetTester.pump(); + final counter = find.text('0'); + + // assert + expect(counterButton, findsOneWidget); + expect(counter, findsOneWidget); + }); + + testWidgets('Should reset counter', (widgetTester) async { + // act + await widgetTester.pumpWidget(tasbihView); + final counterButton = find.byKey(const Key('ResetButton')); + await widgetTester.tap(counterButton); + await widgetTester.pump(); + final counter = find.text('0'); + + // assert + expect(counterButton, findsOneWidget); + expect(counter, findsOneWidget); + }); + testWidgets('Should change target by selected button', (widgetTester) async { + // act + await widgetTester.pumpWidget(tasbihView); + final targetButton = find.byKey(const Key('TargetButton99')); + await widgetTester.tap(targetButton); + await widgetTester.pump(); + final target = find.text('99'); + + // assert + expect(targetButton, findsOneWidget); + expect(target, findsNWidgets(2)); + expect(tasbihController.target, 99); + expect(tasbihController.textEditingController.text, '99'); + }); +} diff --git a/test/helper/test_helper.dart b/test/helper/test_helper.dart new file mode 100644 index 0000000..2db1ba0 --- /dev/null +++ b/test/helper/test_helper.dart @@ -0,0 +1,26 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:mockito/annotations.dart'; +import 'package:suji/app/data/datasources/database_helper.dart'; +import 'package:suji/app/data/datasources/shalat_local_data_source.dart'; +import 'package:suji/app/data/datasources/shalat_remote_data_source.dart'; +import 'package:suji/app/data/datasources/surah_local_data_source.dart'; +import 'package:suji/app/data/datasources/surah_remote_data_source.dart'; +import 'package:suji/app/domain/repository/shalat_repository.dart'; +import 'package:suji/app/domain/repository/surah_repository.dart'; +import 'package:suji/app/domain/usescases/get_asmaul_husna_usecase.dart'; + +@GenerateMocks([ + // Data + ShalatRepository, + SurahRepository, + ShalatLocalDataSource, + ShalatRemoteDataSource, + SurahLocalDataSource, + SurahRemoteDataSource, + Connectivity, + DatabaseHelper, + + // Domain + GetAsmaulHusnaUsecase +]) +void main() {} diff --git a/test/helper/test_helper.mocks.dart b/test/helper/test_helper.mocks.dart new file mode 100644 index 0000000..8b35b83 --- /dev/null +++ b/test/helper/test_helper.mocks.dart @@ -0,0 +1,612 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in suji/test/helper/test_helper.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i10; + +import 'package:connectivity_plus/connectivity_plus.dart' as _i22; +import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart' + as _i23; +import 'package:dartz/dartz.dart' as _i2; +import 'package:geolocator/geolocator.dart' as _i13; +import 'package:isar/isar.dart' as _i25; +import 'package:mockito/mockito.dart' as _i1; +import 'package:suji/app/data/datasources/database_helper.dart' as _i24; +import 'package:suji/app/data/datasources/shalat_local_data_source.dart' + as _i18; +import 'package:suji/app/data/datasources/shalat_remote_data_source.dart' + as _i19; +import 'package:suji/app/data/datasources/surah_local_data_source.dart' as _i20; +import 'package:suji/app/data/datasources/surah_remote_data_source.dart' + as _i21; +import 'package:suji/app/data/model/asmaul_husna_response.dart' as _i4; +import 'package:suji/app/data/model/daily_pray_response.dart' as _i5; +import 'package:suji/app/data/model/list_surah_response.dart' as _i6; +import 'package:suji/app/data/model/shalat_time_response.dart' as _i3; +import 'package:suji/app/data/model/surah_detail_response.dart' as _i7; +import 'package:suji/app/domain/entities/asmaul_husna.dart' as _i17; +import 'package:suji/app/domain/entities/daily_pray.dart' as _i16; +import 'package:suji/app/domain/entities/shalat.dart' as _i12; +import 'package:suji/app/domain/entities/surah.dart' as _i14; +import 'package:suji/app/domain/entities/surah_detail.dart' as _i15; +import 'package:suji/app/domain/repository/shalat_repository.dart' as _i9; +import 'package:suji/app/domain/repository/surah_repository.dart' as _i8; +import 'package:suji/app/domain/usescases/get_asmaul_husna_usecase.dart' + as _i26; +import 'package:suji/core/utils/failure.dart' as _i11; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeEither_0 extends _i1.SmartFake implements _i2.Either { + _FakeEither_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeShalatTimeResponse_1 extends _i1.SmartFake + implements _i3.ShalatTimeResponse { + _FakeShalatTimeResponse_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeAsmaulHusnaResponse_2 extends _i1.SmartFake + implements _i4.AsmaulHusnaResponse { + _FakeAsmaulHusnaResponse_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDataDailyPrayResponse_3 extends _i1.SmartFake + implements _i5.DataDailyPrayResponse { + _FakeDataDailyPrayResponse_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeListSurah_4 extends _i1.SmartFake implements _i6.ListSurah { + _FakeListSurah_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSurahDetailResponse_5 extends _i1.SmartFake + implements _i7.SurahDetailResponse { + _FakeSurahDetailResponse_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSurahRepository_6 extends _i1.SmartFake + implements _i8.SurahRepository { + _FakeSurahRepository_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [ShalatRepository]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockShalatRepository extends _i1.Mock implements _i9.ShalatRepository { + MockShalatRepository() { + _i1.throwOnMissingStub(this); + } + + @override + _i10.Future<_i2.Either<_i11.Failure, _i12.Shalat>> getShalatTime( + DateTime? dateTime, + _i13.Position? position, + ) => + (super.noSuchMethod( + Invocation.method( + #getShalatTime, + [ + dateTime, + position, + ], + ), + returnValue: _i10.Future<_i2.Either<_i11.Failure, _i12.Shalat>>.value( + _FakeEither_0<_i11.Failure, _i12.Shalat>( + this, + Invocation.method( + #getShalatTime, + [ + dateTime, + position, + ], + ), + )), + ) as _i10.Future<_i2.Either<_i11.Failure, _i12.Shalat>>); +} + +/// A class which mocks [SurahRepository]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSurahRepository extends _i1.Mock implements _i8.SurahRepository { + MockSurahRepository() { + _i1.throwOnMissingStub(this); + } + + @override + _i10.Future<_i2.Either<_i11.Failure, List<_i14.Surah>>> getAllSurah() => + (super.noSuchMethod( + Invocation.method( + #getAllSurah, + [], + ), + returnValue: + _i10.Future<_i2.Either<_i11.Failure, List<_i14.Surah>>>.value( + _FakeEither_0<_i11.Failure, List<_i14.Surah>>( + this, + Invocation.method( + #getAllSurah, + [], + ), + )), + ) as _i10.Future<_i2.Either<_i11.Failure, List<_i14.Surah>>>); + @override + _i10.Future<_i2.Either<_i11.Failure, List<_i14.Surah>>> getSurahByQuery( + String? query) => + (super.noSuchMethod( + Invocation.method( + #getSurahByQuery, + [query], + ), + returnValue: + _i10.Future<_i2.Either<_i11.Failure, List<_i14.Surah>>>.value( + _FakeEither_0<_i11.Failure, List<_i14.Surah>>( + this, + Invocation.method( + #getSurahByQuery, + [query], + ), + )), + ) as _i10.Future<_i2.Either<_i11.Failure, List<_i14.Surah>>>); + @override + _i10.Future<_i2.Either<_i11.Failure, List<_i15.SurahDetail>>> + getAyahBySurahNumber(int? surahNumber) => (super.noSuchMethod( + Invocation.method( + #getAyahBySurahNumber, + [surahNumber], + ), + returnValue: _i10 + .Future<_i2.Either<_i11.Failure, List<_i15.SurahDetail>>>.value( + _FakeEither_0<_i11.Failure, List<_i15.SurahDetail>>( + this, + Invocation.method( + #getAyahBySurahNumber, + [surahNumber], + ), + )), + ) as _i10.Future<_i2.Either<_i11.Failure, List<_i15.SurahDetail>>>); + @override + _i10.Future<_i2.Either<_i11.Failure, List<_i16.DailyPray>>> getDailyPray() => + (super.noSuchMethod( + Invocation.method( + #getDailyPray, + [], + ), + returnValue: + _i10.Future<_i2.Either<_i11.Failure, List<_i16.DailyPray>>>.value( + _FakeEither_0<_i11.Failure, List<_i16.DailyPray>>( + this, + Invocation.method( + #getDailyPray, + [], + ), + )), + ) as _i10.Future<_i2.Either<_i11.Failure, List<_i16.DailyPray>>>); + @override + _i10.Future<_i2.Either<_i11.Failure, List<_i17.AsmaulHusna>>> + getAsmaulHusna() => (super.noSuchMethod( + Invocation.method( + #getAsmaulHusna, + [], + ), + returnValue: _i10 + .Future<_i2.Either<_i11.Failure, List<_i17.AsmaulHusna>>>.value( + _FakeEither_0<_i11.Failure, List<_i17.AsmaulHusna>>( + this, + Invocation.method( + #getAsmaulHusna, + [], + ), + )), + ) as _i10.Future<_i2.Either<_i11.Failure, List<_i17.AsmaulHusna>>>); +} + +/// A class which mocks [ShalatLocalDataSource]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockShalatLocalDataSource extends _i1.Mock + implements _i18.ShalatLocalDataSource { + MockShalatLocalDataSource() { + _i1.throwOnMissingStub(this); + } + + @override + _i10.Future<_i12.Shalat?> getShalatTime( + DateTime? dateTime, + _i13.Position? position, + ) => + (super.noSuchMethod( + Invocation.method( + #getShalatTime, + [ + dateTime, + position, + ], + ), + returnValue: _i10.Future<_i12.Shalat?>.value(), + ) as _i10.Future<_i12.Shalat?>); + @override + _i10.Future<_i12.Shalat?> getShalatTimeByDate(DateTime? date) => + (super.noSuchMethod( + Invocation.method( + #getShalatTimeByDate, + [date], + ), + returnValue: _i10.Future<_i12.Shalat?>.value(), + ) as _i10.Future<_i12.Shalat?>); + @override + _i10.Future insertOrUpdateShalat(List<_i12.Shalat>? shalat) => + (super.noSuchMethod( + Invocation.method( + #insertOrUpdateShalat, + [shalat], + ), + returnValue: _i10.Future.value(''), + ) as _i10.Future); +} + +/// A class which mocks [ShalatRemoteDataSource]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockShalatRemoteDataSource extends _i1.Mock + implements _i19.ShalatRemoteDataSource { + MockShalatRemoteDataSource() { + _i1.throwOnMissingStub(this); + } + + @override + _i10.Future<_i3.ShalatTimeResponse> getShalatTime( + DateTime? dateTime, + _i13.Position? position, + ) => + (super.noSuchMethod( + Invocation.method( + #getShalatTime, + [ + dateTime, + position, + ], + ), + returnValue: + _i10.Future<_i3.ShalatTimeResponse>.value(_FakeShalatTimeResponse_1( + this, + Invocation.method( + #getShalatTime, + [ + dateTime, + position, + ], + ), + )), + ) as _i10.Future<_i3.ShalatTimeResponse>); +} + +/// A class which mocks [SurahLocalDataSource]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSurahLocalDataSource extends _i1.Mock + implements _i20.SurahLocalDataSource { + MockSurahLocalDataSource() { + _i1.throwOnMissingStub(this); + } + + @override + _i10.Future> getAllSurah() => (super.noSuchMethod( + Invocation.method( + #getAllSurah, + [], + ), + returnValue: _i10.Future>.value(<_i14.Surah>[]), + ) as _i10.Future>); + @override + _i10.Future<_i4.AsmaulHusnaResponse> getAsmaulHusna() => (super.noSuchMethod( + Invocation.method( + #getAsmaulHusna, + [], + ), + returnValue: _i10.Future<_i4.AsmaulHusnaResponse>.value( + _FakeAsmaulHusnaResponse_2( + this, + Invocation.method( + #getAsmaulHusna, + [], + ), + )), + ) as _i10.Future<_i4.AsmaulHusnaResponse>); + @override + _i10.Future> getSurahByQuery(String? query) => + (super.noSuchMethod( + Invocation.method( + #getSurahByQuery, + [query], + ), + returnValue: _i10.Future>.value(<_i14.Surah>[]), + ) as _i10.Future>); + @override + _i10.Future insertOrUpdateSurah(List<_i14.Surah>? listSurah) => + (super.noSuchMethod( + Invocation.method( + #insertOrUpdateSurah, + [listSurah], + ), + returnValue: _i10.Future.value(''), + ) as _i10.Future); + @override + _i10.Future insertOrUpdateAyah(List<_i15.SurahDetail>? listAyah) => + (super.noSuchMethod( + Invocation.method( + #insertOrUpdateAyah, + [listAyah], + ), + returnValue: _i10.Future.value(''), + ) as _i10.Future); + @override + _i10.Future<_i5.DataDailyPrayResponse> getDailyPray() => (super.noSuchMethod( + Invocation.method( + #getDailyPray, + [], + ), + returnValue: _i10.Future<_i5.DataDailyPrayResponse>.value( + _FakeDataDailyPrayResponse_3( + this, + Invocation.method( + #getDailyPray, + [], + ), + )), + ) as _i10.Future<_i5.DataDailyPrayResponse>); + @override + _i10.Future> getAyahBySurahNumber(int? surahNumber) => + (super.noSuchMethod( + Invocation.method( + #getAyahBySurahNumber, + [surahNumber], + ), + returnValue: + _i10.Future>.value(<_i15.SurahDetail>[]), + ) as _i10.Future>); +} + +/// A class which mocks [SurahRemoteDataSource]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSurahRemoteDataSource extends _i1.Mock + implements _i21.SurahRemoteDataSource { + MockSurahRemoteDataSource() { + _i1.throwOnMissingStub(this); + } + + @override + _i10.Future<_i6.ListSurah> getAllSurah() => (super.noSuchMethod( + Invocation.method( + #getAllSurah, + [], + ), + returnValue: _i10.Future<_i6.ListSurah>.value(_FakeListSurah_4( + this, + Invocation.method( + #getAllSurah, + [], + ), + )), + ) as _i10.Future<_i6.ListSurah>); + @override + _i10.Future<_i7.SurahDetailResponse> getAyahBySurahNumber(int? number) => + (super.noSuchMethod( + Invocation.method( + #getAyahBySurahNumber, + [number], + ), + returnValue: _i10.Future<_i7.SurahDetailResponse>.value( + _FakeSurahDetailResponse_5( + this, + Invocation.method( + #getAyahBySurahNumber, + [number], + ), + )), + ) as _i10.Future<_i7.SurahDetailResponse>); +} + +/// A class which mocks [Connectivity]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockConnectivity extends _i1.Mock implements _i22.Connectivity { + MockConnectivity() { + _i1.throwOnMissingStub(this); + } + + @override + _i10.Stream<_i23.ConnectivityResult> get onConnectivityChanged => + (super.noSuchMethod( + Invocation.getter(#onConnectivityChanged), + returnValue: _i10.Stream<_i23.ConnectivityResult>.empty(), + ) as _i10.Stream<_i23.ConnectivityResult>); + @override + _i10.Future<_i23.ConnectivityResult> checkConnectivity() => + (super.noSuchMethod( + Invocation.method( + #checkConnectivity, + [], + ), + returnValue: _i10.Future<_i23.ConnectivityResult>.value( + _i23.ConnectivityResult.bluetooth), + ) as _i10.Future<_i23.ConnectivityResult>); +} + +/// A class which mocks [DatabaseHelper]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDatabaseHelper extends _i1.Mock implements _i24.DatabaseHelper { + MockDatabaseHelper() { + _i1.throwOnMissingStub(this); + } + + @override + _i10.Future<_i25.Isar?> get database => (super.noSuchMethod( + Invocation.getter(#database), + returnValue: _i10.Future<_i25.Isar?>.value(), + ) as _i10.Future<_i25.Isar?>); + @override + _i10.Future<_i12.Shalat?> getShalatTime(int? id) => (super.noSuchMethod( + Invocation.method( + #getShalatTime, + [id], + ), + returnValue: _i10.Future<_i12.Shalat?>.value(), + ) as _i10.Future<_i12.Shalat?>); + @override + _i10.Future<_i12.Shalat?> getShalatTimeByDate(String? date) => + (super.noSuchMethod( + Invocation.method( + #getShalatTimeByDate, + [date], + ), + returnValue: _i10.Future<_i12.Shalat?>.value(), + ) as _i10.Future<_i12.Shalat?>); + @override + _i10.Future insertOrUpdateShalat(List<_i12.Shalat>? shalat) => + (super.noSuchMethod( + Invocation.method( + #insertOrUpdateShalat, + [shalat], + ), + returnValue: _i10.Future.value(), + returnValueForMissingStub: _i10.Future.value(), + ) as _i10.Future); + @override + _i10.Future> getAllSurah() => (super.noSuchMethod( + Invocation.method( + #getAllSurah, + [], + ), + returnValue: _i10.Future>.value(<_i14.Surah>[]), + ) as _i10.Future>); + @override + _i10.Future> getAyahBySurahNumber(int? surahNumber) => + (super.noSuchMethod( + Invocation.method( + #getAyahBySurahNumber, + [surahNumber], + ), + returnValue: + _i10.Future>.value(<_i15.SurahDetail>[]), + ) as _i10.Future>); + @override + _i10.Future insertOrUpdateSurah(List<_i14.Surah>? listSurah) => + (super.noSuchMethod( + Invocation.method( + #insertOrUpdateSurah, + [listSurah], + ), + returnValue: _i10.Future.value(), + returnValueForMissingStub: _i10.Future.value(), + ) as _i10.Future); + @override + _i10.Future insertOrUpdateAyah(List<_i15.SurahDetail>? listAyah) => + (super.noSuchMethod( + Invocation.method( + #insertOrUpdateAyah, + [listAyah], + ), + returnValue: _i10.Future.value(), + returnValueForMissingStub: _i10.Future.value(), + ) as _i10.Future); + @override + _i10.Future> getSurahByQuery(String? query) => + (super.noSuchMethod( + Invocation.method( + #getSurahByQuery, + [query], + ), + returnValue: _i10.Future>.value(<_i14.Surah>[]), + ) as _i10.Future>); +} + +/// A class which mocks [GetAsmaulHusnaUsecase]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockGetAsmaulHusnaUsecase extends _i1.Mock + implements _i26.GetAsmaulHusnaUsecase { + MockGetAsmaulHusnaUsecase() { + _i1.throwOnMissingStub(this); + } + + @override + _i8.SurahRepository get surahRepository => (super.noSuchMethod( + Invocation.getter(#surahRepository), + returnValue: _FakeSurahRepository_6( + this, + Invocation.getter(#surahRepository), + ), + ) as _i8.SurahRepository); + @override + _i10.Future<_i2.Either<_i11.Failure, List<_i17.AsmaulHusna>>> invoke() => + (super.noSuchMethod( + Invocation.method( + #invoke, + [], + ), + returnValue: + _i10.Future<_i2.Either<_i11.Failure, List<_i17.AsmaulHusna>>>.value( + _FakeEither_0<_i11.Failure, List<_i17.AsmaulHusna>>( + this, + Invocation.method( + #invoke, + [], + ), + )), + ) as _i10.Future<_i2.Either<_i11.Failure, List<_i17.AsmaulHusna>>>); +} diff --git a/test/json_reader.dart b/test/json_reader.dart new file mode 100644 index 0000000..055cb99 --- /dev/null +++ b/test/json_reader.dart @@ -0,0 +1,9 @@ +import 'dart:io'; + +String readJson(String name) { + var dir = Directory.current.path; + if (dir.endsWith('/test')) { + dir = dir.replaceAll('/test', ''); + } + return File('$dir/assets/model/$name').readAsStringSync(); +}