Add local task datasource and repository
- Create TaskLocalDataSource for Drift database operations - Implement LocalTaskRepositoryImpl with same interface as remote - Support full CRUD operations for offline task management
This commit is contained in:
parent
ccc7d544db
commit
ece34acd85
149
lib/features/tasks/data/datasources/task_local_datasource.dart
Normal file
149
lib/features/tasks/data/datasources/task_local_datasource.dart
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
import '../../../../core/database/app_database.dart';
|
||||||
|
import '../../domain/enums/priority.dart';
|
||||||
|
import '../models/task_model.dart';
|
||||||
|
|
||||||
|
abstract class TaskLocalDataSource {
|
||||||
|
Future<List<TaskModel>> getTasksByDate(String date, {String? status});
|
||||||
|
Future<TaskModel> getTaskById(String id);
|
||||||
|
Future<TaskModel> createTask(TaskModel task);
|
||||||
|
Future<TaskModel> updateTask(TaskModel task);
|
||||||
|
Future<void> deleteTask(String id);
|
||||||
|
Future<TaskModel> toggleTaskStatus(String id);
|
||||||
|
Future<TaskModel> rescheduleTask(String id, String targetDate);
|
||||||
|
Future<List<TaskModel>> getAllTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaskLocalDataSourceImpl implements TaskLocalDataSource {
|
||||||
|
final AppDatabase database;
|
||||||
|
|
||||||
|
TaskLocalDataSourceImpl({required this.database});
|
||||||
|
|
||||||
|
TaskModel _taskFromDb(Task dbTask) {
|
||||||
|
return TaskModel(
|
||||||
|
id: dbTask.id,
|
||||||
|
title: dbTask.title,
|
||||||
|
description: dbTask.description,
|
||||||
|
date: dbTask.date,
|
||||||
|
time: dbTask.time,
|
||||||
|
priority: Priority.fromString(dbTask.priority),
|
||||||
|
isDone: dbTask.isDone,
|
||||||
|
createdAt: dbTask.createdAt,
|
||||||
|
updatedAt: dbTask.updatedAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TasksCompanion _taskToDb(TaskModel task) {
|
||||||
|
return TasksCompanion(
|
||||||
|
id: Value(task.id),
|
||||||
|
title: Value(task.title),
|
||||||
|
description: Value(task.description),
|
||||||
|
date: Value(task.date),
|
||||||
|
time: Value(task.time),
|
||||||
|
priority: Value(task.priority.name),
|
||||||
|
isDone: Value(task.isDone),
|
||||||
|
createdAt: Value(task.createdAt ?? DateTime.now()),
|
||||||
|
updatedAt: Value(DateTime.now()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<TaskModel>> getTasksByDate(String date, {String? status}) async {
|
||||||
|
final dateTime = DateTime.parse(date);
|
||||||
|
final tasks = await database.getTasksByDate(dateTime);
|
||||||
|
|
||||||
|
var result = tasks.map(_taskFromDb).toList();
|
||||||
|
|
||||||
|
if (status != null && status != 'all') {
|
||||||
|
if (status == 'completed') {
|
||||||
|
result = result.where((t) => t.isDone).toList();
|
||||||
|
} else if (status == 'active') {
|
||||||
|
result = result.where((t) => !t.isDone).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TaskModel> getTaskById(String id) async {
|
||||||
|
final task = await database.getTaskById(id);
|
||||||
|
if (task == null) {
|
||||||
|
throw Exception('Task not found');
|
||||||
|
}
|
||||||
|
return _taskFromDb(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TaskModel> createTask(TaskModel task) async {
|
||||||
|
final companion = _taskToDb(task);
|
||||||
|
await database.insertTask(companion);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TaskModel> updateTask(TaskModel task) async {
|
||||||
|
final updatedTask = TaskModel(
|
||||||
|
id: task.id,
|
||||||
|
title: task.title,
|
||||||
|
description: task.description,
|
||||||
|
date: task.date,
|
||||||
|
time: task.time,
|
||||||
|
priority: task.priority,
|
||||||
|
isDone: task.isDone,
|
||||||
|
createdAt: task.createdAt,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
final companion = _taskToDb(updatedTask);
|
||||||
|
await database.updateTask(companion);
|
||||||
|
return updatedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteTask(String id) async {
|
||||||
|
await database.deleteTaskById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TaskModel> toggleTaskStatus(String id) async {
|
||||||
|
final task = await getTaskById(id);
|
||||||
|
final updated = TaskModel(
|
||||||
|
id: task.id,
|
||||||
|
title: task.title,
|
||||||
|
description: task.description,
|
||||||
|
date: task.date,
|
||||||
|
time: task.time,
|
||||||
|
priority: task.priority,
|
||||||
|
isDone: !task.isDone,
|
||||||
|
createdAt: task.createdAt,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
await updateTask(updated);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TaskModel> rescheduleTask(String id, String targetDate) async {
|
||||||
|
final task = await getTaskById(id);
|
||||||
|
final updated = TaskModel(
|
||||||
|
id: task.id,
|
||||||
|
title: task.title,
|
||||||
|
description: task.description,
|
||||||
|
date: DateTime.parse(targetDate),
|
||||||
|
time: task.time,
|
||||||
|
priority: task.priority,
|
||||||
|
isDone: task.isDone,
|
||||||
|
createdAt: task.createdAt,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
await updateTask(updated);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<TaskModel>> getAllTasks() async {
|
||||||
|
final tasks = await database.getAllTasks();
|
||||||
|
return tasks.map(_taskFromDb).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import '../../../../core/errors/failures.dart';
|
||||||
|
import '../../../../core/errors/result.dart';
|
||||||
|
import '../../../../core/logging/app_logger.dart';
|
||||||
|
import '../../domain/entities/task_entity.dart';
|
||||||
|
import '../../domain/repositories/task_repository.dart';
|
||||||
|
import '../datasources/task_local_datasource.dart';
|
||||||
|
import '../models/task_model.dart';
|
||||||
|
|
||||||
|
class LocalTaskRepositoryImpl implements TaskRepository {
|
||||||
|
final TaskLocalDataSource localDataSource;
|
||||||
|
final AppLogger logger;
|
||||||
|
|
||||||
|
LocalTaskRepositoryImpl({
|
||||||
|
required this.localDataSource,
|
||||||
|
required this.logger,
|
||||||
|
});
|
||||||
|
|
||||||
|
String _formatDate(DateTime date) {
|
||||||
|
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<List<TaskEntity>>> getTasksByDate(DateTime date, {String? status}) async {
|
||||||
|
try {
|
||||||
|
final tasks = await localDataSource.getTasksByDate(
|
||||||
|
_formatDate(date),
|
||||||
|
status: status,
|
||||||
|
);
|
||||||
|
logger.info('Loaded ${tasks.length} tasks for ${_formatDate(date)} (local)');
|
||||||
|
return Success(tasks);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
logger.error('Local storage error', e, stackTrace);
|
||||||
|
return Error(UnexpectedFailure(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<TaskEntity>> getTaskById(String id) async {
|
||||||
|
try {
|
||||||
|
final task = await localDataSource.getTaskById(id);
|
||||||
|
return Success(task);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
logger.error('Local storage error', e, stackTrace);
|
||||||
|
if (e.toString().contains('not found')) {
|
||||||
|
return Error(NotFoundFailure(message: 'Task not found'));
|
||||||
|
}
|
||||||
|
return Error(UnexpectedFailure(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<TaskEntity>> createTask(TaskEntity task) async {
|
||||||
|
try {
|
||||||
|
final model = TaskModel.fromEntity(task);
|
||||||
|
final created = await localDataSource.createTask(model);
|
||||||
|
logger.info('Created task: ${created.id} (local)');
|
||||||
|
return Success(created);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
logger.error('Local storage error', e, stackTrace);
|
||||||
|
return Error(UnexpectedFailure(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<TaskEntity>> updateTask(TaskEntity task) async {
|
||||||
|
try {
|
||||||
|
final model = TaskModel.fromEntity(task);
|
||||||
|
final updated = await localDataSource.updateTask(model);
|
||||||
|
logger.info('Updated task: ${updated.id} (local)');
|
||||||
|
return Success(updated);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
logger.error('Local storage error', e, stackTrace);
|
||||||
|
if (e.toString().contains('not found')) {
|
||||||
|
return Error(NotFoundFailure(message: 'Task not found'));
|
||||||
|
}
|
||||||
|
return Error(UnexpectedFailure(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<void>> deleteTask(String id) async {
|
||||||
|
try {
|
||||||
|
await localDataSource.deleteTask(id);
|
||||||
|
logger.info('Deleted task: $id (local)');
|
||||||
|
return const Success(null);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
logger.error('Local storage error', e, stackTrace);
|
||||||
|
return Error(UnexpectedFailure(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<TaskEntity>> toggleTaskStatus(String id) async {
|
||||||
|
try {
|
||||||
|
final task = await localDataSource.toggleTaskStatus(id);
|
||||||
|
logger.info('Toggled task status: $id -> ${task.isDone} (local)');
|
||||||
|
return Success(task);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
logger.error('Local storage error', e, stackTrace);
|
||||||
|
if (e.toString().contains('not found')) {
|
||||||
|
return Error(NotFoundFailure(message: 'Task not found'));
|
||||||
|
}
|
||||||
|
return Error(UnexpectedFailure(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<TaskEntity>> rescheduleTask(String id, DateTime targetDate) async {
|
||||||
|
try {
|
||||||
|
final task = await localDataSource.rescheduleTask(id, _formatDate(targetDate));
|
||||||
|
logger.info('Rescheduled task: $id -> ${_formatDate(targetDate)} (local)');
|
||||||
|
return Success(task);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
logger.error('Local storage error', e, stackTrace);
|
||||||
|
if (e.toString().contains('not found')) {
|
||||||
|
return Error(NotFoundFailure(message: 'Task not found'));
|
||||||
|
}
|
||||||
|
return Error(UnexpectedFailure(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user