Polish navigation, Quran flows, and sharing UX
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../data/services/notification_orchestrator_service.dart';
|
||||
import '../../../data/services/notification_event_producer_service.dart';
|
||||
import '../../../data/services/myquran_sholat_service.dart';
|
||||
import '../../../data/services/notification_service.dart';
|
||||
import '../../../data/services/prayer_service.dart';
|
||||
import '../../../data/services/location_service.dart';
|
||||
import '../../../data/local/hive_boxes.dart';
|
||||
@@ -25,7 +30,8 @@ class DaySchedule {
|
||||
final String province;
|
||||
final String date; // yyyy-MM-dd
|
||||
final String tanggal; // formatted date from API
|
||||
final Map<String, String> times; // {imsak, subuh, terbit, dhuha, dzuhur, ashar, maghrib, isya}
|
||||
final Map<String, String>
|
||||
times; // {imsak, subuh, terbit, dhuha, dzuhur, ashar, maghrib, isya}
|
||||
|
||||
DaySchedule({
|
||||
required this.cityName,
|
||||
@@ -65,7 +71,8 @@ class DaySchedule {
|
||||
activeIndex = 1; // 0=Imsak, 1=Subuh
|
||||
} else {
|
||||
for (int i = 0; i < prayers.length; i++) {
|
||||
if (prayers[i].time != '-' && prayers[i].time.compareTo(currentTime) > 0) {
|
||||
if (prayers[i].time != '-' &&
|
||||
prayers[i].time.compareTo(currentTime) > 0) {
|
||||
activeIndex = i;
|
||||
break;
|
||||
}
|
||||
@@ -135,10 +142,10 @@ final prayerTimesProvider = FutureProvider<DaySchedule?>((ref) async {
|
||||
// All prayers passed, fetch tomorrow's schedule
|
||||
final tomorrow = DateTime.now().add(const Duration(days: 1));
|
||||
final tomorrowStr = DateFormat('yyyy-MM-dd').format(tomorrow);
|
||||
|
||||
final tmrwJadwal =
|
||||
await MyQuranSholatService.instance.getDailySchedule(cityId, tomorrowStr);
|
||||
|
||||
|
||||
final tmrwJadwal = await MyQuranSholatService.instance
|
||||
.getDailySchedule(cityId, tomorrowStr);
|
||||
|
||||
if (tmrwJadwal != null) {
|
||||
final cityInfo = await MyQuranSholatService.instance.getCityInfo(cityId);
|
||||
schedule = DaySchedule(
|
||||
@@ -152,37 +159,106 @@ final prayerTimesProvider = FutureProvider<DaySchedule?>((ref) async {
|
||||
}
|
||||
|
||||
if (schedule != null) {
|
||||
unawaited(_syncAdhanNotifications(cityId, schedule));
|
||||
return schedule;
|
||||
}
|
||||
|
||||
// Fallback to adhan package
|
||||
final position = await LocationService.instance.getCurrentLocation();
|
||||
double lat = position?.latitude ?? -6.2088;
|
||||
double lng = position?.longitude ?? 106.8456;
|
||||
final lat = position?.latitude ?? -6.2088;
|
||||
final lng = position?.longitude ?? 106.8456;
|
||||
final locationUnavailable = position == null;
|
||||
|
||||
final result = PrayerService.instance.getPrayerTimes(lat, lng, DateTime.now());
|
||||
if (result != null) {
|
||||
final timeFormat = DateFormat('HH:mm');
|
||||
return DaySchedule(
|
||||
cityName: 'Jakarta',
|
||||
province: 'DKI Jakarta',
|
||||
date: today,
|
||||
tanggal: DateFormat('EEEE, dd/MM/yyyy').format(DateTime.now()),
|
||||
times: {
|
||||
'imsak': timeFormat.format(result.fajr.subtract(const Duration(minutes: 10))),
|
||||
'subuh': timeFormat.format(result.fajr),
|
||||
'terbit': timeFormat.format(result.sunrise),
|
||||
'dhuha': timeFormat.format(result.sunrise.add(const Duration(minutes: 15))),
|
||||
'dzuhur': timeFormat.format(result.dhuhr),
|
||||
'ashar': timeFormat.format(result.asr),
|
||||
'maghrib': timeFormat.format(result.maghrib),
|
||||
'isya': timeFormat.format(result.isha),
|
||||
},
|
||||
final result =
|
||||
PrayerService.instance.getPrayerTimes(lat, lng, DateTime.now());
|
||||
final timeFormat = DateFormat('HH:mm');
|
||||
final fallbackSchedule = DaySchedule(
|
||||
cityName: 'Jakarta',
|
||||
province: 'DKI Jakarta',
|
||||
date: today,
|
||||
tanggal: DateFormat('EEEE, dd/MM/yyyy').format(DateTime.now()),
|
||||
times: {
|
||||
'imsak':
|
||||
timeFormat.format(result.fajr.subtract(const Duration(minutes: 10))),
|
||||
'subuh': timeFormat.format(result.fajr),
|
||||
'terbit': timeFormat.format(result.sunrise),
|
||||
'dhuha':
|
||||
timeFormat.format(result.sunrise.add(const Duration(minutes: 15))),
|
||||
'dzuhur': timeFormat.format(result.dhuhr),
|
||||
'ashar': timeFormat.format(result.asr),
|
||||
'maghrib': timeFormat.format(result.maghrib),
|
||||
'isya': timeFormat.format(result.isha),
|
||||
},
|
||||
);
|
||||
unawaited(
|
||||
NotificationEventProducerService.instance.emitScheduleFallback(
|
||||
settings: Hive.box<AppSettings>(HiveBoxes.settings).get('default') ??
|
||||
AppSettings(),
|
||||
cityId: cityId,
|
||||
locationUnavailable: locationUnavailable,
|
||||
),
|
||||
);
|
||||
unawaited(_syncAdhanNotifications(cityId, fallbackSchedule));
|
||||
return fallbackSchedule;
|
||||
});
|
||||
|
||||
Future<void> _syncAdhanNotifications(
|
||||
String cityId, DaySchedule schedule) async {
|
||||
try {
|
||||
final settingsBox = Hive.box<AppSettings>(HiveBoxes.settings);
|
||||
final settings = settingsBox.get('default') ?? AppSettings();
|
||||
final adhanEnabled = settings.adhanEnabled.values.any((v) => v);
|
||||
|
||||
if (adhanEnabled) {
|
||||
final permissionStatus =
|
||||
await NotificationService.instance.getPermissionStatus();
|
||||
await NotificationEventProducerService.instance
|
||||
.emitPermissionWarningsIfNeeded(
|
||||
settings: settings,
|
||||
permissionStatus: permissionStatus,
|
||||
);
|
||||
}
|
||||
|
||||
final schedulesByDate = <String, Map<String, String>>{
|
||||
schedule.date: schedule.times,
|
||||
};
|
||||
|
||||
final baseDate = DateTime.tryParse(schedule.date);
|
||||
if (baseDate != null) {
|
||||
final nextDate = DateFormat('yyyy-MM-dd')
|
||||
.format(baseDate.add(const Duration(days: 1)));
|
||||
if (!schedulesByDate.containsKey(nextDate)) {
|
||||
final nextSchedule = await MyQuranSholatService.instance
|
||||
.getDailySchedule(cityId, nextDate);
|
||||
if (nextSchedule != null) {
|
||||
schedulesByDate[nextDate] = nextSchedule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await NotificationService.instance.syncPrayerNotifications(
|
||||
cityId: cityId,
|
||||
adhanEnabled: settings.adhanEnabled,
|
||||
iqamahOffset: settings.iqamahOffset,
|
||||
schedulesByDate: schedulesByDate,
|
||||
);
|
||||
await NotificationService.instance.syncHabitNotifications(
|
||||
settings: settings,
|
||||
);
|
||||
await NotificationOrchestratorService.instance.runPassivePass(
|
||||
settings: settings,
|
||||
);
|
||||
} catch (_) {
|
||||
// Don't block UI when scheduling notifications fails.
|
||||
unawaited(
|
||||
NotificationEventProducerService.instance.emitNotificationSyncFailed(
|
||||
settings: Hive.box<AppSettings>(HiveBoxes.settings).get('default') ??
|
||||
AppSettings(),
|
||||
cityId: cityId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/// Provider for monthly prayer schedule (for Imsakiyah screen).
|
||||
final monthlyScheduleProvider =
|
||||
@@ -200,7 +276,7 @@ final cityNameProvider = FutureProvider<String>((ref) async {
|
||||
if (stored.contains('|')) {
|
||||
return stored.split('|').first;
|
||||
}
|
||||
|
||||
|
||||
final cityId = ref.watch(selectedCityIdProvider);
|
||||
final info = await MyQuranSholatService.instance.getCityInfo(cityId);
|
||||
if (info != null) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user