diff --git a/lib/data/services/hijri_service.dart b/lib/data/services/hijri_service.dart new file mode 100644 index 0000000..ed81802 --- /dev/null +++ b/lib/data/services/hijri_service.dart @@ -0,0 +1,68 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:intl/intl.dart'; + +import '../../core/hijri_date.dart'; + +class HijriCalendarService { + HijriCalendarService._({http.Client? client}) + : _client = client ?? http.Client(); + + static final HijriCalendarService instance = HijriCalendarService._(); + static const String _baseUrl = 'https://api.myquran.com/v3/cal/hijr'; + + final http.Client _client; + final Map _cache = {}; + + Future getHijriLabel(DateTime date) async { + final dateOnly = DateTime(date.year, date.month, date.day); + final dateKey = DateFormat('yyyy-MM-dd').format(dateOnly); + final cached = _cache[dateKey]; + if (cached != null) return cached; + + try { + final response = await _client.get(Uri.parse('$_baseUrl/$dateKey')); + if (response.statusCode == 200) { + final payload = json.decode(response.body) as Map; + final label = parseHijriLabel(payload); + if (label != null) { + _cache[dateKey] = label; + return label; + } + } + } catch (_) { + // Keep UI usable when the device is offline. + } + + final fallback = HijriDateFormatter.format(dateOnly); + _cache[dateKey] = fallback; + return fallback; + } + + static String? parseHijriLabel(Map payload) { + if (payload['status'] != true) return null; + + final data = payload['data']; + if (data is! Map) return null; + + final hijr = data['hijr']; + if (hijr is! Map) return null; + + final today = hijr['today']?.toString().trim(); + if (today != null && today.isNotEmpty) { + final parts = today.split(','); + return parts.length > 1 ? parts.last.trim() : today; + } + + final day = hijr['day']; + final monthName = hijr['monthName']?.toString().trim(); + final year = hijr['year']; + + if (day != null && monthName != null && monthName.isNotEmpty && year != null) { + return '$day $monthName $year H'; + } + + return null; + } +} diff --git a/lib/features/home/jumat_screen.dart b/lib/features/home/jumat_screen.dart index 84e0ab9..66f385a 100644 --- a/lib/features/home/jumat_screen.dart +++ b/lib/features/home/jumat_screen.dart @@ -31,7 +31,8 @@ class JumatScreen extends ConsumerWidget { final timeStr = DateFormat('HH:mm').format(clock); final secStr = DateFormat(':ss').format(clock); final dateGregorian = DateFormat('EEEE, d MMMM yyyy', 'en').format(clock); - final dateHijri = HijriDateFormatter.format(clock); + final dateHijri = + ref.watch(hijriDateProvider).valueOrNull ?? HijriDateFormatter.format(clock); final durToKhutbah = screenData.timeUntilNext ?? const Duration(minutes: 0); final minToKhutbah = durToKhutbah.inMinutes; diff --git a/lib/features/home/main_screen.dart b/lib/features/home/main_screen.dart index 84d8368..972c984 100644 --- a/lib/features/home/main_screen.dart +++ b/lib/features/home/main_screen.dart @@ -39,6 +39,8 @@ class MainScreen extends ConsumerWidget { final timeStr = DateFormat('HH:mm').format(clock); final secStr = DateFormat(':ss').format(clock); final dateGregorian = DateFormat('EEEE, d MMMM yyyy', 'id').format(clock); + final dateHijri = + ref.watch(hijriDateProvider).valueOrNull ?? HijriDateFormatter.format(clock); return Container( color: SacredColors.background, @@ -98,7 +100,7 @@ class MainScreen extends ConsumerWidget { child: Column( children: [ // ── HEADER ── - _buildHeader(context, s, fs, settings, dateGregorian), + _buildHeader(context, s, fs, settings, dateGregorian, dateHijri), // ── CENTER: Clock + Countdown ── Expanded( @@ -211,9 +213,14 @@ class MainScreen extends ConsumerWidget { ); } - Widget _buildHeader(BuildContext context, double s, double fs, AppSettings settings, String dateGregorian) { - final dateHijri = HijriDateFormatter.format(DateTime.now()); - + Widget _buildHeader( + BuildContext context, + double s, + double fs, + AppSettings settings, + String dateGregorian, + String dateHijri, + ) { return Padding( padding: EdgeInsets.only(top: 24 * s, bottom: 8 * s), child: Row( diff --git a/lib/providers.dart b/lib/providers.dart index 65a464e..1570bbf 100644 --- a/lib/providers.dart +++ b/lib/providers.dart @@ -3,7 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'core/enums.dart'; +import 'core/hijri_date.dart'; import 'data/local/models.dart'; +import 'data/services/hijri_service.dart'; import 'data/services/sync_service.dart'; // ────────────────────────────────────────────── @@ -75,6 +77,17 @@ final todayScheduleProvider = Provider((ref) { return SyncService.instance.getTodaySchedule(clock); }); +final hijriDateProvider = FutureProvider((ref) async { + final clock = ref.watch(clockProvider).valueOrNull ?? DateTime.now(); + final dateOnly = DateTime(clock.year, clock.month, clock.day); + + try { + return await HijriCalendarService.instance.getHijriLabel(dateOnly); + } catch (_) { + return HijriDateFormatter.format(dateOnly); + } +}); + // ────────────────────────────────────────────── // SCREEN STATE MACHINE PROVIDER // ────────────────────────────────────────────── diff --git a/test/widget_test.dart b/test/widget_test.dart index 4955133..c5a4e00 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -1,6 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:jamshalat_masjid_screen/core/enums.dart'; import 'package:jamshalat_masjid_screen/data/local/models.dart'; +import 'package:jamshalat_masjid_screen/data/services/hijri_service.dart'; void main() { group('PrayerName display labels', () { @@ -37,4 +38,22 @@ void main() { expect(updated.runningTexts, settings.runningTexts); }); }); + + group('HijriCalendarService parsing', () { + test('extracts hijri label from MyQuran calendar response', () { + final label = HijriCalendarService.parseHijriLabel({ + 'status': true, + 'data': { + 'hijr': { + 'today': 'Senin, 11 Syawal 1447 H', + 'day': 11, + 'monthName': 'Syawal', + 'year': 1447, + }, + }, + }); + + expect(label, '11 Syawal 1447 H'); + }); + }); }