- Murattal: Spotify-style 5-button controls [Shuffle, Prev, Play, Next, Playlist] - Murattal: Animated 7-bar equalizer visualization in player circle - Murattal: Unsplash API background with frosted glass player overlay - Murattal: Transparent AppBar with backdrop blur - Murattal: Surah playlist bottom sheet with full 114 Surah list - Murattal: Auto-play disabled on screen open, enabled on navigation - Murattal: Shuffle mode for random Surah playback - Murattal: Photographer attribution per Unsplash guidelines - Dashboard: Auto-scroll prayer schedule to next active prayer - Fix: setState lifecycle errors on Reading & Murattal screens - Setup: flutter_dotenv, cached_network_image, url_launcher deps
108 lines
3.0 KiB
Dart
108 lines
3.0 KiB
Dart
import 'dart:convert';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
import '../local/hive_boxes.dart';
|
|
import '../local/models/dzikir_counter.dart';
|
|
import 'package:intl/intl.dart';
|
|
|
|
/// Represents a single dzikir item from the bundled JSON.
|
|
class DzikirItem {
|
|
final String id;
|
|
final String arabic;
|
|
final String transliteration;
|
|
final String translation;
|
|
final int targetCount;
|
|
final String? source;
|
|
|
|
DzikirItem({
|
|
required this.id,
|
|
required this.arabic,
|
|
required this.transliteration,
|
|
required this.translation,
|
|
required this.targetCount,
|
|
this.source,
|
|
});
|
|
|
|
factory DzikirItem.fromJson(Map<String, dynamic> json) {
|
|
return DzikirItem(
|
|
id: json['id'] as String,
|
|
arabic: json['arabic'] as String? ?? '',
|
|
transliteration: json['transliteration'] as String? ?? '',
|
|
translation: json['translation'] as String? ?? '',
|
|
targetCount: json['target_count'] as int? ?? 1,
|
|
source: json['source'] as String?,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Types of dzikir sessions.
|
|
enum DzikirType { pagi, petang }
|
|
|
|
/// Service to load dzikir data and manage counters.
|
|
class DzikirService {
|
|
DzikirService._();
|
|
static final DzikirService instance = DzikirService._();
|
|
|
|
final Map<DzikirType, List<DzikirItem>> _cache = {};
|
|
|
|
/// Load dzikir items from bundled JSON.
|
|
Future<List<DzikirItem>> getDzikir(DzikirType type) async {
|
|
if (_cache.containsKey(type)) return _cache[type]!;
|
|
|
|
final path = type == DzikirType.pagi
|
|
? 'assets/dzikir/dzikir_pagi.json'
|
|
: 'assets/dzikir/dzikir_petang.json';
|
|
|
|
try {
|
|
final jsonString = await rootBundle.loadString(path);
|
|
final List<dynamic> data = json.decode(jsonString);
|
|
_cache[type] =
|
|
data.map((d) => DzikirItem.fromJson(d as Map<String, dynamic>)).toList();
|
|
} catch (_) {
|
|
_cache[type] = [];
|
|
}
|
|
|
|
return _cache[type]!;
|
|
}
|
|
|
|
/// Get counters for a specific date from Hive.
|
|
Map<String, int> getCountersForDate(String date) {
|
|
final box = Hive.box<DzikirCounter>(HiveBoxes.dzikirCounters);
|
|
final result = <String, int>{};
|
|
|
|
for (final key in box.keys) {
|
|
final counter = box.get(key);
|
|
if (counter != null && counter.date == date) {
|
|
result[counter.dzikirId] = counter.count;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Increment a dzikir counter for a specific ID on a specific date.
|
|
Future<void> increment(String dzikirId, String date, int target) async {
|
|
final box = Hive.box<DzikirCounter>(HiveBoxes.dzikirCounters);
|
|
final key = '${dzikirId}_$date';
|
|
|
|
final existing = box.get(key);
|
|
if (existing != null) {
|
|
existing.count = (existing.count + 1).clamp(0, target);
|
|
await existing.save();
|
|
} else {
|
|
await box.put(
|
|
key,
|
|
DzikirCounter(
|
|
dzikirId: dzikirId,
|
|
date: date,
|
|
count: 1,
|
|
target: target,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Get today's date string.
|
|
String get todayKey => DateFormat('yyyy-MM-dd').format(DateTime.now());
|
|
}
|