diff --git a/lib/data/database/task_dao.dart b/lib/data/database/task_dao.dart index 79d915d..96c5c4f 100644 --- a/lib/data/database/task_dao.dart +++ b/lib/data/database/task_dao.dart @@ -123,4 +123,11 @@ class TaskDAO { return false; } } + + Future updateTasks(List tasks, bool isCompletedNewValue) async { + for (var task in tasks) { + update(task, isCompletedNewValue); + } + return true; + } } diff --git a/lib/data/model/tasks_complete_status.dart b/lib/data/model/tasks_complete_status.dart new file mode 100644 index 0000000..351a935 --- /dev/null +++ b/lib/data/model/tasks_complete_status.dart @@ -0,0 +1,4 @@ +enum TasksCompleteStatus { + checkAll, + uncheckAll +} \ No newline at end of file diff --git a/lib/data/todo_repository.dart b/lib/data/todo_repository.dart index 260d6ef..d9b65bc 100644 --- a/lib/data/todo_repository.dart +++ b/lib/data/todo_repository.dart @@ -9,6 +9,8 @@ abstract class TodoRepository { Future updateTask(Task task, bool isCompletedNewValue); + Future updateTasks(List tasks, bool isCompletedNewValue); + Future addTask(Task task, int? checklistId); Future deleteTasks(List tasks); @@ -90,4 +92,9 @@ class TodoRepositoryImpl implements TodoRepository { taskTitle: taskTitle, ); } + + @override + Future updateTasks(List tasks, bool isCompletedNewValue) async { + return await _todoDAO.updateTasks(tasks, isCompletedNewValue); + } } diff --git a/lib/domain/format_task_list_message_use_case.dart b/lib/domain/format_task_list_message_use_case.dart deleted file mode 100644 index 2d331b3..0000000 --- a/lib/domain/format_task_list_message_use_case.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:injectable/injectable.dart'; -import 'package:todoapp/data/model/task.dart'; - -abstract class FormatTaskListMessageUseCase { - String formatTaskList({required List tasks}); -} - -@Injectable(as: FormatTaskListMessageUseCase) -class FormatTaskListMessageUseCaseImpl extends FormatTaskListMessageUseCase { - @override - String formatTaskList({required List tasks}) { - var checklist = ''; - - for (var task in tasks) { - if (task.isCompleted == false) { - checklist += '- ${task.title}\n'; - } - } - return checklist; - } -} diff --git a/lib/domain/progress_counter_use_case.dart b/lib/domain/progress_counter_use_case.dart deleted file mode 100644 index 992786f..0000000 --- a/lib/domain/progress_counter_use_case.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:injectable/injectable.dart'; -import 'package:todoapp/data/model/task.dart'; - -abstract class ProgressCounterUseCase { - double calculateProgress({required List tasks}); -} - -@Injectable(as: ProgressCounterUseCase) -class ProgressCounterUseCaseImpl extends ProgressCounterUseCase { - @override - double calculateProgress({required List tasks}) { - int completedTasks = 0; - for (var task in tasks) { - if (task.isCompleted) { - completedTasks++; - } - } - - if (tasks.isNotEmpty) { - return completedTasks / tasks.length.toDouble(); - } else { - return 0.0; - } - } -} diff --git a/lib/domain/should_show_share_button_use_case.dart b/lib/domain/should_show_share_button_use_case.dart deleted file mode 100644 index 2a37862..0000000 --- a/lib/domain/should_show_share_button_use_case.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:injectable/injectable.dart'; -import 'package:todoapp/data/model/task.dart'; - -abstract class ShouldShowShareButtonUseCase { - - bool shouldShowShareButton(List tasks); -} - -@Injectable(as: ShouldShowShareButtonUseCase) -class ShouldShowShareButtonUseCaseImpl extends ShouldShowShareButtonUseCase { - @override - bool shouldShowShareButton(List tasks) { - return tasks.any((task) => task.isCompleted == false); - } -} diff --git a/lib/domain/tasks_comparator_use_case.dart b/lib/domain/task_list_sort_helper.dart similarity index 52% rename from lib/domain/tasks_comparator_use_case.dart rename to lib/domain/task_list_sort_helper.dart index acc718a..d087a1b 100644 --- a/lib/domain/tasks_comparator_use_case.dart +++ b/lib/domain/task_list_sort_helper.dart @@ -1,12 +1,13 @@ import 'package:injectable/injectable.dart'; import 'package:todoapp/data/model/task.dart'; -abstract class TasksComparatorUseCase { +abstract class TaskListSortHelper { bool areThemEqual({required List oldList, required List newList}); + List sortByCompletedStatus(List tasks); } -@Injectable(as: TasksComparatorUseCase) -class TasksComparatorUseCaseImpl extends TasksComparatorUseCase { +@Injectable(as: TaskListSortHelper) +class TaskListSortHelperImpl extends TaskListSortHelper { @override bool areThemEqual({ required List oldList, @@ -29,4 +30,21 @@ class TasksComparatorUseCaseImpl extends TasksComparatorUseCase { } return true; } + + @override + List sortByCompletedStatus(List tasks) { + List tasksToBeSorted = List.from(tasks); + tasksToBeSorted.sort((a, b) => _sort(a, b)); + return tasksToBeSorted; + } + + int _sort(Task a, Task b) { + if (a.isCompleted == false && b.isCompleted) { + return -1; + } else if (a.isCompleted && b.isCompleted == false) { + return 1; + } else { + return 0; + } + } } diff --git a/lib/domain/task_list_summary_helper.dart b/lib/domain/task_list_summary_helper.dart new file mode 100644 index 0000000..cc766d2 --- /dev/null +++ b/lib/domain/task_list_summary_helper.dart @@ -0,0 +1,62 @@ +import 'package:injectable/injectable.dart'; +import 'package:todoapp/data/model/task.dart'; +import 'package:todoapp/data/model/tasks_complete_status.dart'; + +abstract class TaskListSummaryHelper { + bool shouldShowShareButton({required List tasks}); + double calculateProgress({required List tasks}); + String formatTaskList({required List tasks}); + TasksCompleteStatus? checkStatus({required List tasks}); +} + +@Injectable(as: TaskListSummaryHelper) +class TaskListSummaryHelperImpl extends TaskListSummaryHelper { + @override + double calculateProgress({required List tasks}) { + int completedTasks = 0; + for (var task in tasks) { + if (task.isCompleted) { + completedTasks++; + } + } + + if (tasks.isNotEmpty) { + return completedTasks / tasks.length.toDouble(); + } else { + return 0.0; + } + } + + @override + String formatTaskList({required List tasks}) { + var checklist = ''; + + for (var task in tasks) { + if (task.isCompleted == false) { + checklist += '- ${task.title}\n'; + } + } + return checklist; + } + + @override + bool shouldShowShareButton({required List tasks}) { + return tasks.any((task) => task.isCompleted == false); + } + + @override + TasksCompleteStatus? checkStatus({required List tasks}) { + if (tasks.isEmpty) { + return null; + } else { + final areAllCompleted = + tasks.where((task) => task.isCompleted).length == tasks.length; + + if (areAllCompleted) { + return TasksCompleteStatus.uncheckAll; + } else { + return TasksCompleteStatus.checkAll; + } + } + } +} diff --git a/lib/domain/tasks_sorter_use_case.dart b/lib/domain/tasks_sorter_use_case.dart deleted file mode 100644 index a3c111c..0000000 --- a/lib/domain/tasks_sorter_use_case.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:injectable/injectable.dart'; -import 'package:todoapp/data/model/task.dart'; - -abstract class TasksSorterUseCase { - List sortByCompletedStatus(List tasks); -} - -@Injectable(as: TasksSorterUseCase) -class TasksSorterUseCaseImpl implements TasksSorterUseCase { - @override - List sortByCompletedStatus(List tasks) { - List tasksToBeSorted = List.from(tasks); - tasksToBeSorted.sort((a, b) => _sort(a, b)); - return tasksToBeSorted; - } - - int _sort(Task a, Task b) { - if (a.isCompleted == false && b.isCompleted) { - return -1; - } else if (a.isCompleted && b.isCompleted == false) { - return 1; - } else { - return 0; - } - } -} \ No newline at end of file diff --git a/lib/ui/components/widgets/check_all_action_chip_widget.dart b/lib/ui/components/widgets/check_all_action_chip_widget.dart new file mode 100644 index 0000000..58b2cf9 --- /dev/null +++ b/lib/ui/components/widgets/check_all_action_chip_widget.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:todoapp/data/model/tasks_complete_status.dart'; +import 'package:todoapp/ui/l10n/app_localizations.dart'; + +class CheckAllActionChipWidget extends StatelessWidget { + final TasksCompleteStatus status; + final VoidCallback onClick; + + const CheckAllActionChipWidget({ + super.key, + required this.status, + required this.onClick, + }); + + @override + Widget build(BuildContext context) { + final localizations = AppLocalizations.of(context)!; + + String text; + switch (status) { + case TasksCompleteStatus.checkAll: + text = localizations.check_all; + case TasksCompleteStatus.uncheckAll: + text = localizations.uncheck_all; + } + return ActionChip( + label: Text(text), + onPressed: () { + onClick(); + }, + ); + } +} diff --git a/lib/ui/components/widgets/checklist/checklist_full_widget.dart b/lib/ui/components/widgets/checklist/checklist_full_widget.dart index cace041..d36b2c0 100644 --- a/lib/ui/components/widgets/checklist/checklist_full_widget.dart +++ b/lib/ui/components/widgets/checklist/checklist_full_widget.dart @@ -38,12 +38,9 @@ class ChecklistsListFullWidgetState extends State { final getIt = GetItStartupHandlerWrapper.getIt; _tasksViewModel = TasksViewModel( repository: getIt.get(), + taskListSummaryHelper: getIt.get(), shareMessageHandler: getIt.get(), - shouldShowShareButtonUseCase: getIt.get(), - formatTaskListMessageUseCase: getIt.get(), - tasksSorterUseCase: getIt.get(), - tasksComparatorUseCase: getIt.get(), - progressCounterUseCase: getIt.get(), + taskListSortHelper: getIt.get(), ); } @@ -103,23 +100,22 @@ class ChecklistsListFullWidgetState extends State { itemCount: widget.checklists.length, ), ), - Expanded( - flex: 6, - child: TasksListWidget( - tasks: tasks == null ? [] : tasks!, - emptyTasksMessage: localizations.empty_tasks, - onCompleteTask: _tasksViewModel.onCompleteTask, - onRemoveTask: (task) => _showConfirmationDialogToRemoveTask( - context, - task, - ), - onReorder: _tasksViewModel.reorder, - onTap: (task) => _navigateToTaskScreen( - context, - checklistId: selected?.id, - task: task, - ), - )), + TasksListWidget( + flex: 6, + tasks: tasks == null ? [] : tasks!, + emptyTasksMessage: localizations.empty_tasks, + onCompleteTask: _tasksViewModel.onCompleteTask, + onRemoveTask: (task) => _showConfirmationDialogToRemoveTask( + context, + task, + ), + onReorder: _tasksViewModel.reorder, + onTap: (task) => _navigateToTaskScreen( + context, + checklistId: selected?.id, + task: task, + ), + ), ], ); } diff --git a/lib/ui/components/widgets/task/taskslist/tasks_list_widget.dart b/lib/ui/components/widgets/task/taskslist/tasks_list_widget.dart index e40bf0a..9701d65 100644 --- a/lib/ui/components/widgets/task/taskslist/tasks_list_widget.dart +++ b/lib/ui/components/widgets/task/taskslist/tasks_list_widget.dart @@ -4,6 +4,7 @@ import 'package:todoapp/ui/components/widgets/task/task_cell_widget.dart'; class TasksListWidget extends StatelessWidget { final List tasks; + final int? flex; final String emptyTasksMessage; final Function(Task) onRemoveTask; final Function(Task p1, bool p2) onCompleteTask; @@ -13,6 +14,7 @@ class TasksListWidget extends StatelessWidget { const TasksListWidget({ super.key, required this.tasks, + this.flex, required this.emptyTasksMessage, required this.onRemoveTask, required this.onCompleteTask, @@ -26,8 +28,9 @@ class TasksListWidget extends StatelessWidget { } Widget _buildTaskList(BuildContext context) { + Widget child; if (tasks.isEmpty) { - return Center( + child = Center( child: Text( emptyTasksMessage, textAlign: TextAlign.center, @@ -35,7 +38,7 @@ class TasksListWidget extends StatelessWidget { ), ); } else { - return ReorderableListView.builder( + child = ReorderableListView.builder( onReorder: onReorder, padding: const EdgeInsets.only( top: 12, @@ -47,6 +50,11 @@ class TasksListWidget extends StatelessWidget { itemCount: tasks.length, ); } + + return Expanded( + flex: flex ?? 1, + child: child, + ); } TaskCellWidget _buildTaskCellWidget(Task task) { diff --git a/lib/ui/components/widgets/task/taskslist/tasks_screen_state.dart b/lib/ui/components/widgets/task/taskslist/tasks_screen_state.dart index 407277d..56efd48 100644 --- a/lib/ui/components/widgets/task/taskslist/tasks_screen_state.dart +++ b/lib/ui/components/widgets/task/taskslist/tasks_screen_state.dart @@ -1,5 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:todoapp/data/model/task.dart'; +import 'package:todoapp/data/model/tasks_complete_status.dart'; part 'tasks_screen_state.freezed.dart'; @@ -14,10 +15,14 @@ class TasksScreenState with _$TasksScreenState { @override final bool showShareIcon; + @override + final TasksCompleteStatus? tasksCompleteStatus; + const TasksScreenState({ required this.tasks, required this.isLoading, required this.progress, required this.showShareIcon, + this.tasksCompleteStatus, }); } diff --git a/lib/ui/components/widgets/task/taskslist/tasks_viewmodel.dart b/lib/ui/components/widgets/task/taskslist/tasks_viewmodel.dart index 057716b..d99d5b1 100644 --- a/lib/ui/components/widgets/task/taskslist/tasks_viewmodel.dart +++ b/lib/ui/components/widgets/task/taskslist/tasks_viewmodel.dart @@ -1,33 +1,24 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:todoapp/data/model/task.dart'; +import 'package:todoapp/data/model/tasks_complete_status.dart'; import 'package:todoapp/data/todo_repository.dart'; -import 'package:todoapp/domain/format_task_list_message_use_case.dart'; -import 'package:todoapp/domain/progress_counter_use_case.dart'; -import 'package:todoapp/domain/should_show_share_button_use_case.dart'; -import 'package:todoapp/domain/tasks_comparator_use_case.dart'; -import 'package:todoapp/domain/tasks_sorter_use_case.dart'; +import 'package:todoapp/domain/task_list_sort_helper.dart'; +import 'package:todoapp/domain/task_list_summary_helper.dart'; import 'package:todoapp/ui/components/widgets/task/taskslist/tasks_screen_state.dart'; import 'package:todoapp/util/share_message_handler.dart'; class TasksViewModel extends Cubit { late TodoRepository _repository; late ShareMessageHandler _shareMessageHandler; - - ShouldShowShareButtonUseCase shouldShowShareButtonUseCase; - TasksSorterUseCase tasksSorterUseCase; - TasksComparatorUseCase tasksComparatorUseCase; - ProgressCounterUseCase progressCounterUseCase; - FormatTaskListMessageUseCase formatTaskListMessageUseCase; + late TaskListSummaryHelper _taskListSummaryHelper; + late TaskListSortHelper _taskListSortHelper; TasksViewModel({ required TodoRepository repository, required ShareMessageHandler shareMessageHandler, - required this.shouldShowShareButtonUseCase, - required this.tasksSorterUseCase, - required this.tasksComparatorUseCase, - required this.progressCounterUseCase, - required this.formatTaskListMessageUseCase, + required TaskListSummaryHelper taskListSummaryHelper, + required TaskListSortHelper taskListSortHelper, }) : super( const TasksScreenState( tasks: [], @@ -38,6 +29,8 @@ class TasksViewModel extends Cubit { ) { _repository = repository; _shareMessageHandler = shareMessageHandler; + _taskListSummaryHelper = taskListSummaryHelper; + _taskListSortHelper = taskListSortHelper; } void _onLoad() { @@ -46,24 +39,43 @@ class TasksViewModel extends Cubit { ); } + Future onCompleteButtonAction(int? checklistId) async { + TasksCompleteStatus? status = _taskListSummaryHelper.checkStatus( + tasks: state.tasks, + ); + + await _repository.updateTasks( + state.tasks, + status == TasksCompleteStatus.checkAll, + ); + + await updateTasks(checklistId); + } + Future updateTasks(int? checklistId) async { _onLoad(); var tasks = await _repository.getTasks(checklistId); + + TasksCompleteStatus? status = _taskListSummaryHelper.checkStatus( + tasks: tasks, + ); + emit( state.copyWith( isLoading: false, tasks: tasks, - showShareIcon: shouldShowShareButtonUseCase.shouldShowShareButton( - tasks + tasksCompleteStatus: status, + showShareIcon: _taskListSummaryHelper.shouldShowShareButton( + tasks: tasks, ), - progress: progressCounterUseCase.calculateProgress(tasks: tasks), + progress: _taskListSummaryHelper.calculateProgress(tasks: tasks), ), ); } Future shareTasks({required String checklistName}) async { - final checklist = formatTaskListMessageUseCase.formatTaskList( + final checklist = _taskListSummaryHelper.formatTaskList( tasks: state.tasks, ); @@ -88,11 +100,14 @@ class TasksViewModel extends Cubit { tasks[index] = tasks[index].copyWith(isCompleted: value); emit( state.copyWith( - progress: progressCounterUseCase.calculateProgress(tasks: tasks), - showShareIcon: shouldShowShareButtonUseCase.shouldShowShareButton( - tasks + progress: _taskListSummaryHelper.calculateProgress(tasks: tasks), + showShareIcon: _taskListSummaryHelper.shouldShowShareButton( + tasks: tasks, ), isLoading: false, + tasksCompleteStatus: _taskListSummaryHelper.checkStatus( + tasks: tasks, + ), tasks: tasks, ), ); @@ -110,12 +125,14 @@ class TasksViewModel extends Cubit { tasks.remove(task); emit( state.copyWith( - progress: progressCounterUseCase.calculateProgress(tasks: tasks), - showShareIcon: shouldShowShareButtonUseCase.shouldShowShareButton( - tasks - ), - isLoading: false, - tasks: tasks), + progress: _taskListSummaryHelper.calculateProgress(tasks: tasks), + showShareIcon: _taskListSummaryHelper.shouldShowShareButton( + tasks: tasks, + ), + isLoading: false, + tasksCompleteStatus: _taskListSummaryHelper.checkStatus(tasks: tasks), + tasks: tasks, + ), ); } } @@ -135,8 +152,8 @@ class TasksViewModel extends Cubit { emit( state.copyWith( isLoading: false, - showShareIcon: shouldShowShareButtonUseCase.shouldShowShareButton( - tasks + showShareIcon: _taskListSummaryHelper.shouldShowShareButton( + tasks: tasks, ), tasks: tasks, ), @@ -146,11 +163,11 @@ class TasksViewModel extends Cubit { } void onSort() { - List tasksToBeSorted = tasksSorterUseCase.sortByCompletedStatus( + List tasksToBeSorted = _taskListSortHelper.sortByCompletedStatus( state.tasks, ); - bool wasSortedPerformed = tasksComparatorUseCase.areThemEqual( + bool wasSortedPerformed = _taskListSortHelper.areThemEqual( oldList: state.tasks, newList: tasksToBeSorted, ); diff --git a/lib/ui/l10n/app_en.arb b/lib/ui/l10n/app_en.arb index c2149c8..848af80 100644 --- a/lib/ui/l10n/app_en.arb +++ b/lib/ui/l10n/app_en.arb @@ -20,5 +20,7 @@ "remove": "Remove", "sort_message": "It is sorted", "share": "Share", - "sort": "Sort" + "sort": "Sort", + "check_all": "Check all", + "uncheck_all": "Uncheck all" } \ No newline at end of file diff --git a/lib/ui/l10n/app_pt.arb b/lib/ui/l10n/app_pt.arb index 89250c7..0d3849f 100644 --- a/lib/ui/l10n/app_pt.arb +++ b/lib/ui/l10n/app_pt.arb @@ -20,5 +20,7 @@ "remove": "Deletar", "sort_message": "Está ordenado", "share": "Compartilhar", - "sort": "Ordenar" + "sort": "Ordenar", + "check_all": "Complete tudo", + "uncheck_all": "Limpar tudo" } \ No newline at end of file diff --git a/lib/ui/screens/tasks/tasks_screen.dart b/lib/ui/screens/tasks/tasks_screen.dart index 46e2210..c3ec05d 100644 --- a/lib/ui/screens/tasks/tasks_screen.dart +++ b/lib/ui/screens/tasks/tasks_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:todoapp/data/model/checklist.dart'; import 'package:todoapp/data/model/task.dart'; import 'package:todoapp/ui/components/remove_task_dialog_builder.dart'; +import 'package:todoapp/ui/components/widgets/check_all_action_chip_widget.dart'; import 'package:todoapp/ui/components/widgets/custom_app_bar_widget.dart'; import 'package:todoapp/ui/components/widgets/progress_widget.dart'; import 'package:todoapp/ui/components/widgets/task/taskslist/tasks_list_widget.dart'; @@ -32,11 +33,8 @@ class TasksScreen extends StatelessWidget { final viewModel = TasksViewModel( repository: getIt.get(), shareMessageHandler: getIt.get(), - shouldShowShareButtonUseCase: getIt.get(), - formatTaskListMessageUseCase: getIt.get(), - tasksSorterUseCase: getIt.get(), - tasksComparatorUseCase: getIt.get(), - progressCounterUseCase: getIt.get(), + taskListSummaryHelper: getIt.get(), + taskListSortHelper: getIt.get(), ); viewModel.updateTasks(checklist.id); @@ -54,6 +52,9 @@ class TasksScreen extends StatelessWidget { updateTasks: viewModel.updateTasks, onReorder: viewModel.reorder, onSort: () => {viewModel.onSort()}, + onCompleteButtonAction: () { + viewModel.onCompleteButtonAction(checklist.id); + }, onShare: () => { viewModel.shareTasks(checklistName: checklist.title), }, @@ -111,37 +112,42 @@ class TasksScaffold extends StatelessWidget { ); }, ), - body: Stack( + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only( - left: 12, - right: 12, - ), - child: TasksListWidget( - tasks: uiState.tasks, - emptyTasksMessage: localizations.empty_tasks, - onReorder: callbacks.onReorder, - onRemoveTask: (task) => - _showConfirmationDialogToRemoveTask(context, task), - onCompleteTask: callbacks.onCompleteTask, - onTap: (task) => { - _navigateToTaskScreen( - context, - checklistId: checklistId, - task: task, - ) - }, - ), - ), - Padding( - padding: const EdgeInsets.only( - bottom: 12, + bottom: 8, ), child: ProgressWidget( progress: uiState.progress, ), ), + Padding( + padding: const EdgeInsets.only(left: 12, right: 12, bottom: 8), + child: uiState.tasksCompleteStatus != null + ? CheckAllActionChipWidget( + status: uiState.tasksCompleteStatus!, + onClick: () { + callbacks.onCompleteButtonAction(); + }, + ) + : const SizedBox.shrink()), + TasksListWidget( + tasks: uiState.tasks, + emptyTasksMessage: localizations.empty_tasks, + onReorder: callbacks.onReorder, + onRemoveTask: (task) => + _showConfirmationDialogToRemoveTask(context, task), + onCompleteTask: callbacks.onCompleteTask, + onTap: (task) => { + _navigateToTaskScreen( + context, + checklistId: checklistId, + task: task, + ) + }, + ), ], ), ); diff --git a/lib/ui/screens/tasks/tasks_screen_callbacks.dart b/lib/ui/screens/tasks/tasks_screen_callbacks.dart index ce23894..5414f56 100644 --- a/lib/ui/screens/tasks/tasks_screen_callbacks.dart +++ b/lib/ui/screens/tasks/tasks_screen_callbacks.dart @@ -3,12 +3,14 @@ import 'package:todoapp/data/model/task.dart'; class TasksScreenCallbacks { final Function(int?) updateTasks; final Function(Task, bool) onCompleteTask; + final Function onCompleteButtonAction; final Function(Task) onRemoveTask; final Function(int oldIndex, int newIndex) onReorder; final Function() onShare; final Function() onSort; const TasksScreenCallbacks({ + required this.onCompleteButtonAction, required this.updateTasks, required this.onCompleteTask, required this.onRemoveTask, diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 57d2162..f76975f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,10 @@ import FlutterMacOS import Foundation -import path_provider_foundation import share_plus import sqflite_darwin func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 53f18cb..ab64c03 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "85.0.0" + version: "91.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "7.7.1" + version: "8.4.1" ansicolor: dependency: transitive description: @@ -37,26 +37,26 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.13.1" auto_route: dependency: "direct main" description: name: auto_route - sha256: "6d3ccc11b520b6eff0ab5a2c3d1c43c46d1486249cc746c4bb14486d876e8b43" + sha256: e9acfeb3df33d188fce4ad0239ef4238f333b7aa4d95ec52af3c2b9360dcd969 url: "https://pub.dev" source: hosted - version: "10.3.0" + version: "11.1.0" auto_route_generator: dependency: "direct dev" description: name: auto_route_generator - sha256: "4d78093dc102eef57c9d53e43454d735f1552039dc9c595ef8cfc7445d9017f2" + sha256: "04300eaf5821962aae8b5cd94f67013fd2fd326dc3be212d3ec1ae7470f09834" url: "https://pub.dev" source: hosted - version: "10.2.6" + version: "10.4.0" bloc: dependency: transitive description: @@ -77,18 +77,18 @@ packages: dependency: transitive description: name: build - sha256: ce76b1d48875e3233fde17717c23d1f60a91cc631597e49a400c89b475395b1d + sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.5" build_config: dependency: transitive description: name: build_config - sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" + sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" build_daemon: dependency: transitive description: @@ -97,30 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.1" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: d1d57f7807debd7349b4726a19fd32ec8bc177c71ad0febf91a20f84cd2d4b46 - url: "https://pub.dev" - source: hosted - version: "3.0.3" build_runner: dependency: "direct dev" description: name: build_runner - sha256: b24597fceb695969d47025c958f3837f9f0122e237c6a22cb082a5ac66c3ca30 + sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" url: "https://pub.dev" source: hosted - version: "2.7.1" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "066dda7f73d8eb48ba630a55acb50c4a84a2e6b453b1cb4567f581729e794f7b" - url: "https://pub.dev" - source: hosted - version: "9.3.1" + version: "2.13.1" built_collection: dependency: transitive description: @@ -133,18 +117,18 @@ packages: dependency: transitive description: name: built_value - sha256: "7931c90b84bc573fef103548e354258ae4c9d28d140e41961df6843c5d60d4d8" + sha256: "0730c18c770d05636a8f945c32a4d7d81cb6e0f0148c8db4ad12e7748f7e49af" url: "https://pub.dev" source: hosted - version: "8.12.3" + version: "8.12.5" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" checked_yaml: dependency: transitive description: @@ -173,10 +157,10 @@ packages: dependency: transitive description: name: code_assets - sha256: ae0db647e668cbb295a3527f0938e4039e004c80099dce2f964102373f5ce0b5 + sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" url: "https://pub.dev" source: hosted - version: "0.19.10" + version: "1.0.0" code_builder: dependency: transitive description: @@ -213,10 +197,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "701dcfc06da0882883a2657c445103380e53e647060ad8d9dfb710c100996608" + sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" url: "https://pub.dev" source: hosted - version: "0.3.5+1" + version: "0.3.5+2" crypto: dependency: transitive description: @@ -229,26 +213,26 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.9" dart_style: dependency: transitive description: name: dart_style - sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.3" eagle_eye: dependency: "direct dev" description: name: eagle_eye - sha256: "9373c193d5706b112a51afff4d4f20fa5b21cc375e609828c0d2b80c0cab4e1c" + sha256: "1ffec524a01531d385f12b30e4f2ab067b9c6f5f39442afd69d68df0f01a5d37" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" equatable: dependency: "direct main" description: @@ -269,10 +253,10 @@ packages: dependency: transitive description: name: ffi - sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" file: dependency: transitive description: @@ -361,10 +345,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: ae78de7c3f2304b8d81f2bb6e320833e5e81de942188542328f074978cc0efa9 + sha256: "568d62f0e68666fb5d95519743b3c24a34c7f19d834b0658c46e26d778461f66" url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "9.2.1" glob: dependency: transitive description: @@ -385,10 +369,10 @@ packages: dependency: transitive description: name: hooks - sha256: "5410b9f4f6c9f01e8ff0eb81c9801ea13a3c3d39f8f0b1613cda08e27eab3c18" + sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388 url: "https://pub.dev" source: hosted - version: "0.20.5" + version: "1.0.2" hotreloader: dependency: transitive description: @@ -425,10 +409,10 @@ packages: dependency: "direct dev" description: name: injectable_generator - sha256: "09c55dba52b53d17411b90134a6751270b8930abd2529e2637d700fc99b0d5b5" + sha256: "309c3f3546160dd00b575f16b341a6a3025479950441bcc7fcb2f8404a40d326" url: "https://pub.dev" source: hosted - version: "2.8.1" + version: "2.9.1" intl: dependency: "direct main" description: @@ -445,22 +429,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" - js: - dependency: transitive - description: - name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.dev" - source: hosted - version: "0.7.2" json_annotation: dependency: transitive description: name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 url: "https://pub.dev" source: hosted - version: "4.9.0" + version: "4.11.0" leak_tracker: dependency: transitive description: @@ -489,18 +465,18 @@ packages: dependency: transitive description: name: lean_builder - sha256: ef5cd5f907157eb7aa87d1704504b5a6386d2cbff88a3c2b3344477bab323ee9 + sha256: "4f3d70c34c52cc5034e8cc6f53d35aa3a32fb373b78fb4c29cf45cd1dcf06942" url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.1.5" lints: dependency: transitive description: name: lints - sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.1.0" logging: dependency: transitive description: @@ -513,18 +489,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.18" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: @@ -545,10 +521,10 @@ packages: dependency: transitive description: name: native_toolchain_c - sha256: f8872ea6c7a50ce08db9ae280ca2b8efdd973157ce462826c82f3c3051d154ce + sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572" url: "https://pub.dev" source: hosted - version: "0.17.2" + version: "0.17.6" nested: dependency: transitive description: @@ -569,10 +545,10 @@ packages: dependency: transitive description: name: objective_c - sha256: "55eb67ede1002d9771b3f9264d2c9d30bc364f0267bc1c6cc0883280d5f0c7cb" + sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" url: "https://pub.dev" source: hosted - version: "9.2.2" + version: "9.3.0" package_config: dependency: transitive description: @@ -601,10 +577,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba" url: "https://pub.dev" source: hosted - version: "2.2.22" + version: "2.2.23" path_provider_foundation: dependency: transitive description: @@ -750,10 +726,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "7b19d6ba131c6eb98bfcbf8d56c1a7002eba438af2e7ae6f8398b2b0f4f381e3" + sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.2.2" source_map_stack_trace: dependency: transitive description: @@ -774,10 +750,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.2" sqflite: dependency: "direct main" description: @@ -790,10 +766,10 @@ packages: dependency: transitive description: name: sqflite_android - sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 + sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" url: "https://pub.dev" source: hosted - version: "2.4.2+2" + version: "2.4.2+3" sqflite_common: dependency: transitive description: @@ -870,34 +846,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" + sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a" url: "https://pub.dev" source: hosted - version: "1.26.3" + version: "1.29.0" test_api: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.9" test_core: dependency: transitive description: name: test_core - sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" - url: "https://pub.dev" - source: hosted - version: "0.6.12" - timing: - dependency: transitive - description: - name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "0.6.15" typed_data: dependency: transitive description: @@ -942,10 +910,10 @@ packages: dependency: transitive description: name: uuid - sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 + sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" url: "https://pub.dev" source: hosted - version: "4.5.2" + version: "4.5.3" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 87f1f0e..f860e81 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+16 +version: 1.2.0 environment: sdk: ^3.5.0 @@ -38,7 +38,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 - get_it: ^8.0.3 + get_it: ^9.2.1 equatable: ^2.0.7 flutter_bloc: ^9.1.1 sqflite: ^2.4.2 @@ -47,7 +47,7 @@ dependencies: freezed: ^3.0.6 path: ^1.9.1 share_plus: 12.0.1 - auto_route: ^10.1.0+1 + auto_route: ^11.1.0 flutter_expandable_fab: ^2.5.2 dev_dependencies: @@ -64,7 +64,7 @@ dev_dependencies: injectable_generator: ^2.5.0 build_runner: ^2.5.4 auto_route_generator: ^10.1.0 - eagle_eye: ^2.0.0 + eagle_eye: ^2.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/test/domain/format_task_list_message_use_case_test.dart b/test/domain/format_task_list_message_use_case_test.dart deleted file mode 100644 index 6f0066e..0000000 --- a/test/domain/format_task_list_message_use_case_test.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:todoapp/data/model/task.dart'; -import 'package:todoapp/domain/format_task_list_message_use_case.dart'; - -void main() { - test( - 'formatTaskList -> test empty task list', - () { - // Arrange - final formatTaskListMessageUseCase = FormatTaskListMessageUseCaseImpl(); - - // Act - final result = formatTaskListMessageUseCase.formatTaskList( - tasks: [], - ); - - // Assert - expect(result, ''); - }, - ); - - test( - 'formatTaskList -> test only not completed', - () { - // Arrange - final formatTaskListMessageUseCase = FormatTaskListMessageUseCaseImpl(); - - // Act - final result = formatTaskListMessageUseCase.formatTaskList( - tasks: [ - const Task( - id: null, - title: 'Task A - Not completed', - isCompleted: false, - ), - const Task( - id: null, - title: 'Task B - Not completed', - isCompleted: false, - ), - const Task( - id: null, - title: 'Task C - Completed', - isCompleted: true, - ), - ], - ); - - // Assert - expect( - result, - '- Task A - Not completed\n' - '- Task B - Not completed\n', - ); - }, - ); -} diff --git a/test/domain/progress_counter_use_case_test.dart b/test/domain/progress_counter_use_case_test.dart deleted file mode 100644 index 64e7f3a..0000000 --- a/test/domain/progress_counter_use_case_test.dart +++ /dev/null @@ -1,158 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:todoapp/data/model/task.dart'; -import 'package:todoapp/domain/progress_counter_use_case.dart'; - -void main() { - test( - 'calculateProgress -> test empty task list', - () { - // Arrange - final progressCounterUseCase = ProgressCounterUseCaseImpl(); - - // Act - final result = progressCounterUseCase.calculateProgress( - tasks: [], - ); - - // Assert - expect(result, 0.0); - }, - ); - - test( - 'calculateProgress -> test a positive progress', - () { - // Arrange - final progressCounterUseCase = ProgressCounterUseCaseImpl(); - - // Act - final result = progressCounterUseCase.calculateProgress( - tasks: [ - const Task( - id: null, - title: 'Task A - Not completed', - isCompleted: false, - ), - const Task( - id: null, - title: 'Task B - Completed', - isCompleted: true, - ), - const Task( - id: null, - title: 'Task C - Completed', - isCompleted: true, - ), - ], - ); - - // Assert - expect( - result, - 0.6666666666666666, - ); - }, - ); - - test( - 'calculateProgress -> test a bad progress', - () { - // Arrange - final progressCounterUseCase = ProgressCounterUseCaseImpl(); - - // Act - final result = progressCounterUseCase.calculateProgress( - tasks: [ - const Task( - id: null, - title: 'Task A - Not completed', - isCompleted: false, - ), - const Task( - id: null, - title: 'Task B - Not Completed', - isCompleted: false, - ), - const Task( - id: null, - title: 'Task C - Completed', - isCompleted: true, - ), - ], - ); - - // Assert - expect( - result, - 0.3333333333333333, - ); - }, - ); - - test( - 'calculateProgress -> test a great progress', - () { - // Arrange - final progressCounterUseCase = ProgressCounterUseCaseImpl(); - - // Act - final result = progressCounterUseCase.calculateProgress( - tasks: [ - const Task( - id: null, - title: 'Task A - Completed', - isCompleted: true, - ), - const Task( - id: null, - title: 'Task B - Completed', - isCompleted: true, - ), - const Task( - id: null, - title: 'Task C - Completed', - isCompleted: true, - ), - ], - ); - - // Assert - expect( - result, - 1.0, - ); - }, - ); - - test( - 'calculateProgress -> test a horrible progress', - () { - // Arrange - final progressCounterUseCase = ProgressCounterUseCaseImpl(); - - // Act - final result = progressCounterUseCase.calculateProgress( - tasks: [ - const Task( - id: null, - title: 'Task A - Not Completed', - isCompleted: false, - ), - const Task( - id: null, - title: 'Task B - Not Completed', - isCompleted: false, - ), - const Task( - id: null, - title: 'Task C - Not Completed', - isCompleted: false, - ), - ], - ); - - // Assert - expect(result, 0.0); - }, - ); -} diff --git a/test/domain/should_show_share_button_use_case_test.dart b/test/domain/should_show_share_button_use_case_test.dart deleted file mode 100644 index f002967..0000000 --- a/test/domain/should_show_share_button_use_case_test.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:todoapp/data/model/task.dart'; -import 'package:todoapp/domain/should_show_share_button_use_case.dart'; - -void main() { - test( - 'shouldShowShareButton -> test empty task list', - () { - // Arrange - final shouldShowShareButtonUseCase = ShouldShowShareButtonUseCaseImpl(); - - // Act - final result = shouldShowShareButtonUseCase.shouldShowShareButton([]); - - // Assert - expect(result, false); - }, - ); - - test( - 'shouldShowShareButton -> there is a task not completed', - () { - // Arrange - final shouldShowShareButtonUseCase = ShouldShowShareButtonUseCaseImpl(); - - // Act - final result = shouldShowShareButtonUseCase.shouldShowShareButton( - [ - const Task( - id: null, - title: 'Task A - Not completed', - isCompleted: false, - ), - const Task( - id: null, - title: 'Task B - Completed', - isCompleted: true, - ), - const Task( - id: null, - title: 'Task C - Completed', - isCompleted: true, - ), - ], - ); - - // Assert - expect( - result, - true, - ); - }, - ); - - test( - 'shouldShowShareButton -> there is none not completed task', - () { - // Arrange - final shouldShowShareButtonUseCase = ShouldShowShareButtonUseCaseImpl(); - - // Act - final result = shouldShowShareButtonUseCase.shouldShowShareButton( - [ - const Task( - id: null, - title: 'Task A -Completed', - isCompleted: true, - ), - const Task( - id: null, - title: 'Task B - Completed', - isCompleted: true, - ), - ], - ); - - // Assert - expect( - result, - false, - ); - }, - ); -} diff --git a/test/domain/tasks_comparator_use_case_test.dart b/test/domain/tasks_comparator_use_case_test.dart deleted file mode 100644 index 05b8625..0000000 --- a/test/domain/tasks_comparator_use_case_test.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:todoapp/data/model/task.dart'; -import 'package:todoapp/domain/tasks_comparator_use_case.dart'; - -void main() { - test( - 'areThemEqual -> test empty task list', - () { - // Arrange - final tasksComparatorUseCase = TasksComparatorUseCaseImpl(); - - // Act - final result = tasksComparatorUseCase.areThemEqual( - oldList: [], - newList: [], - ); - - // Assert - expect(result, true); - }, - ); - - test( - 'areThemEqual -> test different sizes', - () { - // Arrange - final tasksComparatorUseCase = TasksComparatorUseCaseImpl(); - - // Act - final result = tasksComparatorUseCase.areThemEqual( - oldList: [const Task(isCompleted: false, title: 'test', id: 1)], - newList: [ - const Task(isCompleted: false, title: 'test', id: 1), - const Task(isCompleted: true, title: 'test2', id: 2) - ], - ); - - // Assert - expect(result, false); - }, - ); - - test( - 'areThemEqual -> test same size with different elements', - () { - // Arrange - final tasksComparatorUseCase = TasksComparatorUseCaseImpl(); - - // Act - final result = tasksComparatorUseCase.areThemEqual( - oldList: [ - const Task(isCompleted: true, title: 'test2', id: 2), - const Task(isCompleted: false, title: 'test1', id: 1) - ], - newList: [ - const Task(isCompleted: false, title: 'test1', id: 1), - const Task(isCompleted: true, title: 'test2', id: 2), - ], - ); - - // Assert - expect(result, false); - }, - ); - - test( - 'areThemEqual -> test same size with equal elements but different values', - () { - // Arrange - final tasksComparatorUseCase = TasksComparatorUseCaseImpl(); - - // Act - final result = tasksComparatorUseCase.areThemEqual( - oldList: [ - const Task(isCompleted: true, title: 'test1', id: 1), - const Task(isCompleted: false, title: 'test2', id: 2) - ], - newList: [ - const Task(isCompleted: true, title: 'test1', id: 1), - const Task(isCompleted: true, title: 'test2', id: 2), - ], - ); - - // Assert - expect(result, false); - }, - ); - - test( - 'areThemEqual -> test same size with equal elements + equal values', - () { - // Arrange - final tasksComparatorUseCase = TasksComparatorUseCaseImpl(); - - // Act - final result = tasksComparatorUseCase.areThemEqual( - oldList: [ - const Task(isCompleted: true, title: 'test1', id: 1), - const Task(isCompleted: false, title: 'test2', id: 2) - ], - newList: [ - const Task(isCompleted: true, title: 'test1', id: 1), - const Task(isCompleted: false, title: 'test2', id: 2), - ], - ); - - // Assert - expect(result, true); - }, - ); -} diff --git a/test/domain/tasks_list_sorter_comparator_test.dart b/test/domain/tasks_list_sorter_comparator_test.dart new file mode 100644 index 0000000..b8f2928 --- /dev/null +++ b/test/domain/tasks_list_sorter_comparator_test.dart @@ -0,0 +1,113 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:todoapp/data/model/task.dart'; +import 'package:todoapp/domain/task_list_sort_helper.dart'; + +void main() { + group('comparator tasks', () { + test( + 'areThemEqual -> test empty task list', + () { + // Arrange + final helper = TaskListSortHelperImpl(); + + // Act + final result = helper.areThemEqual( + oldList: [], + newList: [], + ); + + // Assert + expect(result, true); + }, + ); + + test( + 'areThemEqual -> test different sizes', + () { + // Arrange + final helper = TaskListSortHelperImpl(); + + // Act + final result = helper.areThemEqual( + oldList: [const Task(isCompleted: false, title: 'test', id: 1)], + newList: [ + const Task(isCompleted: false, title: 'test', id: 1), + const Task(isCompleted: true, title: 'test2', id: 2) + ], + ); + + // Assert + expect(result, false); + }, + ); + + test( + 'areThemEqual -> test same size with different elements', + () { + // Arrange + final helper = TaskListSortHelperImpl(); + + // Act + final result = helper.areThemEqual( + oldList: [ + const Task(isCompleted: true, title: 'test2', id: 2), + const Task(isCompleted: false, title: 'test1', id: 1) + ], + newList: [ + const Task(isCompleted: false, title: 'test1', id: 1), + const Task(isCompleted: true, title: 'test2', id: 2), + ], + ); + + // Assert + expect(result, false); + }, + ); + + test( + 'areThemEqual -> test same size with equal elements but different values', + () { + // Arrange + final helper = TaskListSortHelperImpl(); + + // Act + final result = helper.areThemEqual( + oldList: [ + const Task(isCompleted: true, title: 'test1', id: 1), + const Task(isCompleted: false, title: 'test2', id: 2) + ], + newList: [ + const Task(isCompleted: true, title: 'test1', id: 1), + const Task(isCompleted: true, title: 'test2', id: 2), + ], + ); + + // Assert + expect(result, false); + }, + ); + + test( + 'areThemEqual -> test same size with equal elements + equal values', + () { + // Arrange + final helper = TaskListSortHelperImpl(); + + // Act + final result = helper.areThemEqual( + oldList: [ + const Task(isCompleted: true, title: 'test1', id: 1), + const Task(isCompleted: false, title: 'test2', id: 2) + ], + newList: [ + const Task(isCompleted: true, title: 'test1', id: 1), + const Task(isCompleted: false, title: 'test2', id: 2), + ], + ); + + // Assert + expect(result, true); + }, + ); + }); +} diff --git a/test/domain/tasks_list_sorter_sort_test.dart b/test/domain/tasks_list_sorter_sort_test.dart new file mode 100644 index 0000000..f88121c --- /dev/null +++ b/test/domain/tasks_list_sorter_sort_test.dart @@ -0,0 +1,59 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:todoapp/data/model/task.dart'; +import 'package:todoapp/domain/task_list_sort_helper.dart'; + +void main() { + group('sort tasks', () { + test( + 'sortByCompletedStatus -> test empty task list', + () { + // Arrange + final helper = TaskListSortHelperImpl(); + + // Act + final result = helper.sortByCompletedStatus([]); + + // Assert + expect(result, []); + }, + ); + + test( + 'sortByCompletedStatus -> there is a task not completed', + () { + // Arrange + final helper = TaskListSortHelperImpl(); + const taskB = Task( + id: null, + title: 'Task B - Completed', + isCompleted: true, + ); + const taskA = Task( + id: null, + title: 'Task A - Not completed', + isCompleted: false, + ); + const taskC = Task( + id: null, + title: 'Task C - Completed', + isCompleted: true, + ); + + // Act + final result = helper.sortByCompletedStatus( + [ + taskB, + taskA, + taskC, + ], + ); + + // Assert + expect( + result, + [taskA, taskB, taskC], + ); + }, + ); + }); +} diff --git a/test/domain/tasks_list_summary_helper_test.dart b/test/domain/tasks_list_summary_helper_test.dart new file mode 100644 index 0000000..a8a6674 --- /dev/null +++ b/test/domain/tasks_list_summary_helper_test.dart @@ -0,0 +1,300 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:todoapp/data/model/task.dart'; +import 'package:todoapp/domain/task_list_summary_helper.dart'; + +void main() { + group('Format tasks list', () { + test( + 'formatTaskList -> test empty task list', + () { + // Arrange + final formatTaskListMessageUseCase = TaskListSummaryHelperImpl(); + + // Act + final result = formatTaskListMessageUseCase.formatTaskList( + tasks: [], + ); + + // Assert + expect(result, ''); + }, + ); + + test( + 'formatTaskList -> test only not completed', + () { + // Arrange + final formatTaskListMessageUseCase = TaskListSummaryHelperImpl(); + + // Act + final result = formatTaskListMessageUseCase.formatTaskList( + tasks: [ + const Task( + id: null, + title: 'Task A - Not completed', + isCompleted: false, + ), + const Task( + id: null, + title: 'Task B - Not completed', + isCompleted: false, + ), + const Task( + id: null, + title: 'Task C - Completed', + isCompleted: true, + ), + ], + ); + + // Assert + expect( + result, + '- Task A - Not completed\n' + '- Task B - Not completed\n', + ); + }, + ); + }); + + group('should show share button', () { + test( + 'shouldShowShareButton -> test empty task list', + () { + // Arrange + final helper = TaskListSummaryHelperImpl(); + + // Act + final result = helper.shouldShowShareButton( + tasks: [], + ); + + // Assert + expect(result, false); + }, + ); + + test( + 'shouldShowShareButton -> there is a task not completed', + () { + // Arrange + final helper = TaskListSummaryHelperImpl(); + + // Act + final result = helper.shouldShowShareButton( + tasks: [ + const Task( + id: null, + title: 'Task A - Not completed', + isCompleted: false, + ), + const Task( + id: null, + title: 'Task B - Completed', + isCompleted: true, + ), + const Task( + id: null, + title: 'Task C - Completed', + isCompleted: true, + ), + ], + ); + + // Assert + expect( + result, + true, + ); + }, + ); + + test( + 'shouldShowShareButton -> there is none not completed task', + () { + // Arrange + final helper = TaskListSummaryHelperImpl(); + + // Act + final result = helper.shouldShowShareButton( + tasks: [ + const Task( + id: null, + title: 'Task A -Completed', + isCompleted: true, + ), + const Task( + id: null, + title: 'Task B - Completed', + isCompleted: true, + ), + ], + ); + + // Assert + expect( + result, + false, + ); + }, + ); + }); + + group( + 'calculate tasks progress', + () { + test( + 'calculateProgress -> test empty task list', + () { + // Arrange + final helper = TaskListSummaryHelperImpl(); + + // Act + final result = helper.calculateProgress( + tasks: [], + ); + + // Assert + expect(result, 0.0); + }, + ); + + test( + 'calculateProgress -> test a positive progress', + () { + // Arrange + final helper = TaskListSummaryHelperImpl(); + + // Act + final result = helper.calculateProgress( + tasks: [ + const Task( + id: null, + title: 'Task A - Not completed', + isCompleted: false, + ), + const Task( + id: null, + title: 'Task B - Completed', + isCompleted: true, + ), + const Task( + id: null, + title: 'Task C - Completed', + isCompleted: true, + ), + ], + ); + + // Assert + expect( + result, + 0.6666666666666666, + ); + }, + ); + + test( + 'calculateProgress -> test a bad progress', + () { + // Arrange + final helper = TaskListSummaryHelperImpl(); + + // Act + final result = helper.calculateProgress( + tasks: [ + const Task( + id: null, + title: 'Task A - Not completed', + isCompleted: false, + ), + const Task( + id: null, + title: 'Task B - Not Completed', + isCompleted: false, + ), + const Task( + id: null, + title: 'Task C - Completed', + isCompleted: true, + ), + ], + ); + + // Assert + expect( + result, + 0.3333333333333333, + ); + }, + ); + + test( + 'calculateProgress -> test a great progress', + () { + // Arrange + final helper = TaskListSummaryHelperImpl(); + + // Act + final result = helper.calculateProgress( + tasks: [ + const Task( + id: null, + title: 'Task A - Completed', + isCompleted: true, + ), + const Task( + id: null, + title: 'Task B - Completed', + isCompleted: true, + ), + const Task( + id: null, + title: 'Task C - Completed', + isCompleted: true, + ), + ], + ); + + // Assert + expect( + result, + 1.0, + ); + }, + ); + + test( + 'calculateProgress -> test a horrible progress', + () { + // Arrange + final helper = TaskListSummaryHelperImpl(); + + // Act + final result = helper.calculateProgress( + tasks: [ + const Task( + id: null, + title: 'Task A - Not Completed', + isCompleted: false, + ), + const Task( + id: null, + title: 'Task B - Not Completed', + isCompleted: false, + ), + const Task( + id: null, + title: 'Task C - Not Completed', + isCompleted: false, + ), + ], + ); + + // Assert + expect(result, 0.0); + }, + ); + }, + ); +} diff --git a/test/domain/tasks_sorter_use_case_test.dart b/test/domain/tasks_sorter_use_case_test.dart deleted file mode 100644 index bc73607..0000000 --- a/test/domain/tasks_sorter_use_case_test.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:todoapp/data/model/task.dart'; -import 'package:todoapp/domain/tasks_sorter_use_case.dart'; - -void main() { - test( - 'sortByCompletedStatus -> test empty task list', - () { - // Arrange - final tasksSorterUseCase = TasksSorterUseCaseImpl(); - - // Act - final result = tasksSorterUseCase.sortByCompletedStatus([]); - - // Assert - expect(result, []); - }, - ); - - test( - 'sortByCompletedStatus -> there is a task not completed', - () { - // Arrange - final tasksSorterUseCase = TasksSorterUseCaseImpl(); - const taskB = Task( - id: null, - title: 'Task B - Completed', - isCompleted: true, - ); - const taskA = Task( - id: null, - title: 'Task A - Not completed', - isCompleted: false, - ); - const taskC = Task( - id: null, - title: 'Task C - Completed', - isCompleted: true, - ); - - // Act - final result = tasksSorterUseCase.sortByCompletedStatus( - [ - taskB, - taskA, - taskC, - ], - ); - - // Assert - expect( - result, - [taskA, taskB, taskC], - ); - }, - ); -} diff --git a/test/test_utils/fakes/fake_callbacks.dart b/test/test_utils/fakes/fake_callbacks.dart index 0ce151a..19ed386 100644 --- a/test/test_utils/fakes/fake_callbacks.dart +++ b/test/test_utils/fakes/fake_callbacks.dart @@ -3,6 +3,7 @@ import 'package:todoapp/ui/screens/tasks/tasks_screen_callbacks.dart'; class FakeCallbacks { static final emptyTasksScreenCallbacks = TasksScreenCallbacks( updateTasks: (_) => const {}, + onCompleteButtonAction: (_) => const {}, onShare: () => const {}, onCompleteTask: (_, __) => const {}, onRemoveTask: (_) => const {}, diff --git a/test/test_utils/fakes/fake_repository.dart b/test/test_utils/fakes/fake_repository.dart index 23974aa..d8d3cea 100644 --- a/test/test_utils/fakes/fake_repository.dart +++ b/test/test_utils/fakes/fake_repository.dart @@ -81,4 +81,12 @@ class FakeRepository implements TodoRepository { return Future.value(false); } } + + @override + Future updateTasks(List tasks, bool isCompletedNewValue) { + for (var task in tasks) { + updateTask(task, isCompletedNewValue); + } + return Future.value(true); + } } diff --git a/test/ui/components/widgets/task/taskslist/tasks_viewmodel_test.dart b/test/ui/components/widgets/task/taskslist/tasks_viewmodel_test.dart index f629897..cf525ec 100644 --- a/test/ui/components/widgets/task/taskslist/tasks_viewmodel_test.dart +++ b/test/ui/components/widgets/task/taskslist/tasks_viewmodel_test.dart @@ -1,10 +1,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:todoapp/data/model/task.dart'; -import 'package:todoapp/domain/format_task_list_message_use_case.dart'; -import 'package:todoapp/domain/progress_counter_use_case.dart'; -import 'package:todoapp/domain/should_show_share_button_use_case.dart'; -import 'package:todoapp/domain/tasks_comparator_use_case.dart'; -import 'package:todoapp/domain/tasks_sorter_use_case.dart'; +import 'package:todoapp/data/model/tasks_complete_status.dart'; +import 'package:todoapp/domain/task_list_sort_helper.dart'; +import 'package:todoapp/domain/task_list_summary_helper.dart'; import 'package:todoapp/ui/components/widgets/task/taskslist/tasks_screen_state.dart'; import 'package:todoapp/ui/components/widgets/task/taskslist/tasks_viewmodel.dart'; @@ -19,12 +17,9 @@ void main() { fakeRepository = FakeRepository(tasks: [], checklists: []); viewModel = TasksViewModel( repository: fakeRepository, - progressCounterUseCase: ProgressCounterUseCaseImpl(), - tasksSorterUseCase: TasksSorterUseCaseImpl(), + taskListSummaryHelper: TaskListSummaryHelperImpl(), shareMessageHandler: FakeShareMessageHandler(), - tasksComparatorUseCase: TasksComparatorUseCaseImpl(), - formatTaskListMessageUseCase: FormatTaskListMessageUseCaseImpl(), - shouldShowShareButtonUseCase: ShouldShowShareButtonUseCaseImpl(), + taskListSortHelper: TaskListSortHelperImpl(), ); }); @@ -69,6 +64,7 @@ void main() { showShareIcon: true, progress: 0, isLoading: false, + tasksCompleteStatus: TasksCompleteStatus.checkAll, ), ); }, @@ -108,6 +104,7 @@ void main() { showShareIcon: false, progress: 1, isLoading: false, + tasksCompleteStatus: TasksCompleteStatus.uncheckAll, ), ); }, @@ -137,6 +134,7 @@ void main() { tasks: [], showShareIcon: false, isLoading: false, + tasksCompleteStatus: null, ), ); },