- Complete MVP with Repository Pattern, SQLite storage - Provider + ChangeNotifier state management - Navigation 2.0 with deep link support - Habit CRUD with twoDayRule, notifications, categories - Backup/Restore via JSON - Statistics with streak tracking - Material You theme support - Biometric lock support - Desktop widget support - 27 languages i18n structure - Comprehensive test suite (87/89 passing)
106 lines
3.1 KiB
Dart
106 lines
3.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:habo/constants.dart';
|
|
import 'package:habo/habits/habit.dart';
|
|
import 'package:habo/model/habit_data.dart';
|
|
import 'package:habo/model/habo_model.dart';
|
|
import 'package:habo/repositories/habit_repository.dart';
|
|
|
|
class SqliteHabitRepository implements HabitRepository {
|
|
final HaboModel _model;
|
|
|
|
SqliteHabitRepository(this._model);
|
|
|
|
@override
|
|
Future<List<Habit>> getAllHabits() async {
|
|
final maps = await _model.getAllHabits();
|
|
return maps.map((map) {
|
|
final data = _mapToHabitData(map);
|
|
return Habit(habitData: data);
|
|
}).toList();
|
|
}
|
|
|
|
@override
|
|
Future<int> createHabit(Habit habit) async {
|
|
return _model.insertHabit(habit.habitData);
|
|
}
|
|
|
|
@override
|
|
Future<void> updateHabit(Habit habit) async {
|
|
await _model.updateHabit(habit.habitData);
|
|
}
|
|
|
|
@override
|
|
Future<void> deleteHabit(int id) async {
|
|
await _model.deleteHabit(id);
|
|
}
|
|
|
|
@override
|
|
Future<Habit?> findHabitById(int id) async {
|
|
final map = await _model.getHabitById(id);
|
|
if (map == null) return null;
|
|
final data = _mapToHabitData(map);
|
|
return Habit(habitData: data);
|
|
}
|
|
|
|
@override
|
|
Future<void> updateHabitsOrder(List<Habit> habits) async {
|
|
await _model.updateHabitsOrder(habits);
|
|
}
|
|
|
|
@override
|
|
Future<void> deleteAllHabits() async {
|
|
await _model.deleteAllHabits();
|
|
}
|
|
|
|
@override
|
|
Future<void> insertHabits(List<Habit> habits) async {
|
|
for (final habit in habits) {
|
|
await _model.insertHabit(habit.habitData);
|
|
}
|
|
}
|
|
|
|
HabitData _mapToHabitData(Map<String, dynamic> map) {
|
|
return HabitData(
|
|
id: map['id'] as int?,
|
|
position: map['position'] as int? ?? 0,
|
|
title: map['title'] as String? ?? '',
|
|
twoDayRule: (map['twoDayRule'] as int? ?? 0) == 1,
|
|
cue: map['cue'] as String? ?? '',
|
|
routine: map['routine'] as String? ?? '',
|
|
reward: map['reward'] as String? ?? '',
|
|
showReward: (map['showReward'] as int? ?? 0) == 1,
|
|
advanced: (map['advanced'] as int? ?? 0) == 1,
|
|
notification: (map['notification'] as int? ?? 0) == 1,
|
|
notTime: _parseTimeOfDay(map['notTime'] as String? ?? ''),
|
|
sanction: map['sanction'] as String? ?? '',
|
|
showSanction: (map['showSanction'] as int? ?? 0) == 1,
|
|
accountant: map['accountant'] as String? ?? '',
|
|
habitType: _indexToHabitType(map['habitType'] as int? ?? 0),
|
|
targetValue: (map['targetValue'] as num?)?.toDouble() ?? 100.0,
|
|
partialValue: (map['partialValue'] as num?)?.toDouble() ?? 10.0,
|
|
unit: map['unit'] as String? ?? '',
|
|
archived: (map['archived'] as int? ?? 0) == 1,
|
|
);
|
|
}
|
|
|
|
HabitType _indexToHabitType(int index) {
|
|
if (index >= 0 && index < HabitType.values.length) {
|
|
return HabitType.values[index];
|
|
}
|
|
return HabitType.boolean;
|
|
}
|
|
|
|
TimeOfDay _parseTimeOfDay(String timeStr) {
|
|
if (timeStr.isEmpty) return const TimeOfDay(hour: 20, minute: 0);
|
|
try {
|
|
final parts = timeStr.split(':');
|
|
return TimeOfDay(
|
|
hour: int.parse(parts[0]),
|
|
minute: int.parse(parts[1]),
|
|
);
|
|
} catch (_) {
|
|
return const TimeOfDay(hour: 20, minute: 0);
|
|
}
|
|
}
|
|
}
|