diff --git a/lib/src/App.dart b/lib/src/App.dart index df4a1cf..1718897 100644 --- a/lib/src/App.dart +++ b/lib/src/App.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'screens/event_screen.dart'; import 'screens/home_screen.dart'; import 'screens/initial_loading_screen.dart'; import 'screens/login_screen.dart'; @@ -49,6 +50,7 @@ class App extends StatelessWidget { builder: (BuildContext context) { return TaskScreen( isEdit: true, + taskId: routeTokens[1], ); }, ); @@ -58,6 +60,14 @@ class App extends StatelessWidget { return NewImageScreen(); }, ); + } else if (routeTokens.first == 'event') { + return MaterialPageRoute( + builder: (BuildContext context) { + return EventScreen( + eventName: routeTokens[1], + ); + }, + ); } // Default route. return MaterialPageRoute( diff --git a/lib/src/blocs/event_bloc.dart b/lib/src/blocs/event_bloc.dart index a41e2d0..05d52c2 100644 --- a/lib/src/blocs/event_bloc.dart +++ b/lib/src/blocs/event_bloc.dart @@ -1,18 +1,16 @@ import 'dart:async'; +import 'package:meta/meta.dart'; import '../resources/firestore_provider.dart'; import '../resources/firebase_storage_provider.dart'; -import '../services/current_selection_service.dart'; /// A business logic component that manages the state for an event screen. class EventBloc { - /// An instance of the current selection service. - final CurrentSelectionService _selectionService = currentSelectionService; - - /// The name of the event that's currently selected. - /// - /// Read only. - String get selectedEventName => _selectionService.event.name; + /// The name of the event being shown. + final String eventName; + EventBloc({ + @required this.eventName, + }); /// Fetches the tasks for the current user that a part of the currently /// selected event. diff --git a/lib/src/blocs/home_bloc.dart b/lib/src/blocs/home_bloc.dart index aee32ca..a4a22e4 100644 --- a/lib/src/blocs/home_bloc.dart +++ b/lib/src/blocs/home_bloc.dart @@ -5,7 +5,6 @@ import 'package:rxdart/rxdart.dart'; import '../models/task_model.dart'; import '../resources/firestore_provider.dart'; import '../services/auth_service.dart'; -import '../services/current_selection_service.dart'; export '../services/auth_service.dart' show FirebaseUser; @@ -19,9 +18,6 @@ class HomeBloc { /// An instance of the firebase repository. final FirestoreProvider _firestore = firestoreProvider; - /// An instance of the current task service. - final CurrentSelectionService _selectionService = currentSelectionService; - /// A subject of list of task model. final _tasks = BehaviorSubject>(); @@ -113,19 +109,6 @@ class HomeBloc { ); } - /// Sets the global selected task. - void updateSelectedTask(TaskModel task) { - _selectionService.updateSelectedTask(task); - } - - /// Updated the global selected event. - void updateSelectedEvent(TaskModel task) async { - final userModel = await _auth.getCurrentUserModel(); - final event = - await _firestore.getEvent(userModel.id, eventName: task.event); - _selectionService.updateSelectedEvent(event); - } - /// Updates the serach box text. void updateSearchBoxText(String newText) { _searchBoxText.add(newText); diff --git a/lib/src/blocs/task_bloc.dart b/lib/src/blocs/task_bloc.dart index fcfecd9..208f0bd 100644 --- a/lib/src/blocs/task_bloc.dart +++ b/lib/src/blocs/task_bloc.dart @@ -7,7 +7,6 @@ import '../models/task_model.dart'; import '../models/user_model.dart'; import '../resources/firestore_provider.dart'; import '../services/auth_service.dart'; -import '../services/current_selection_service.dart'; /// Business logic component that manages the state for the task screen. class TaskBloc extends Object with Validators { @@ -17,19 +16,24 @@ class TaskBloc extends Object with Validators { /// An instance of the firebase repository. final FirestoreProvider _firestore = firestoreProvider; - /// An instance of the current task service. - final CurrentSelectionService _selectionService = currentSelectionService; - /// A subject of user model. + /// + /// Needed to access the username of the owner of the task. final _user = BehaviorSubject(); /// A subject of task event name. + /// + /// Only needed if in edit mode. This will receive updates when the task to be + /// edited is fetched. final _eventName = BehaviorSubject(); /// A subject of task text. final _taskText = BehaviorSubject(); /// A subject of the text of the current global task. + /// + /// Only needed if in edit mode. This will receive updates when the task to be + /// edited is fetched. final _textInitialValue = BehaviorSubject(); /// The priority of the current task. @@ -37,10 +41,10 @@ class TaskBloc extends Object with Validators { /// The id of the current task. /// - /// Only to be used if editing an existing task. - String taskId; + /// Only needed if in edit mode. Not needed by the UI. + final String taskId; - //Stream getters. + // Stream getters. /// An observable of the current user model. Observable get userModelStream => _user.stream; @@ -48,14 +52,20 @@ class TaskBloc extends Object with Validators { Observable get eventName => _eventName.stream; /// An observable of the current task text. + /// + /// Emits an error if the string added is empty. Observable get taskText => _taskText.stream.transform(validateStringNotEmpty); /// An observable of the submit enabled flag. + /// + /// Only emits true when an event is selected and the task text is not empty. Observable get submitEnabled => Observable.combineLatest2(eventName, taskText, (a, b) => true); /// An observable of the text of the global selected task. + /// + /// Only needed in edit mode. Observable get textInitialvalue => _textInitialValue.stream; //Sinks getters. @@ -65,7 +75,7 @@ class TaskBloc extends Object with Validators { ///Changes the current task text. Function(String) get changeTaskText => _taskText.sink.add; - TaskBloc() { + TaskBloc({this.taskId}) { setCurrentUser(); } @@ -76,7 +86,6 @@ class TaskBloc extends Object with Validators { //TODO: Figure out how to update the event and user properties if needed. // as in the number of pending high tasks for example. - /// Saves or updates the current task in the database. Future submit(isEdit) { if (isEdit) { @@ -105,17 +114,11 @@ class TaskBloc extends Object with Validators { /// Grabs the data from the current global task and pipes it to the local /// streams. - void populateWithCurrentTask() { - // We only want the task that was just selected and stop listening - // after receiving it. - _selectionService.task.take(1).listen( - (TaskModel task) { - _textInitialValue.sink.add(task.text); - changeEventName(task.event); - changeTaskText(task.text); - taskId = task.id; - }, - ); + void populateWithCurrentTask() async { + final task = await _firestore.getTask(taskId); + _textInitialValue.sink.add(task.text); + changeEventName(task.event); + changeTaskText(task.text); } void dispose() { diff --git a/lib/src/resources/firestore_provider.dart b/lib/src/resources/firestore_provider.dart index 3b24ffc..a1a3f31 100644 --- a/lib/src/resources/firestore_provider.dart +++ b/lib/src/resources/firestore_provider.dart @@ -129,7 +129,7 @@ class FirestoreProvider { } /// Returns a Stream of a single task from an id. - Observable getTask(String id) { + Observable getTaskObservable(String id) { final mappedStream = _firestore.collection('tasks').document(id).snapshots().map( (DocumentSnapshot snapshot) { @@ -143,6 +143,16 @@ class FirestoreProvider { return Observable(mappedStream); } + //TODO: Add tests for this method. + /// Returns a task from an id. + Future getTask(String id) async { + final documentSnapshot = await _firestore.document('tasks/$id').get(); + return TaskModel.fromFirestore( + documentSnapshot.data, + id: documentSnapshot.documentID, + ); + } + /// Deletes a task from firestore. Future deleteTask(String id) async { try { diff --git a/lib/src/screens/event_screen.dart b/lib/src/screens/event_screen.dart index 24aaf5d..b72ef2c 100644 --- a/lib/src/screens/event_screen.dart +++ b/lib/src/screens/event_screen.dart @@ -4,15 +4,28 @@ import '../blocs/event_bloc.dart'; import '../widgets/custom_app_bar.dart'; class EventScreen extends StatefulWidget { + /// The name of the event this screenn is showing. + final String eventName; + + EventScreen({ + @required this.eventName, + }); + _EventScreenState createState() => _EventScreenState(); } class _EventScreenState extends State { - final EventBloc bloc = EventBloc(); + EventBloc bloc; + + initState() { + super.initState(); + bloc = EventBloc(eventName: widget.eventName); + } + Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar( - title: '', + title: widget.eventName, ), ); } diff --git a/lib/src/screens/home_screen.dart b/lib/src/screens/home_screen.dart index fc61bd5..e8f9b6c 100644 --- a/lib/src/screens/home_screen.dart +++ b/lib/src/screens/home_screen.dart @@ -93,11 +93,12 @@ class _HomeScreenState extends State { task: task, onDone: () => bloc.markTaskAsDone(task), onEventPressed: () { - bloc.updateSelectedEvent(task); + // Include the event name in the route. + Navigator.of(context).pushNamed('event/${task.event}'); }, onEditPressed: () { - bloc.updateSelectedTask(task); - Navigator.of(context).pushNamed('editTask/'); + // Include the id of the task to be edited in the route. + Navigator.of(context).pushNamed('editTask/${task.id}'); }, ), padding: EdgeInsets.only(bottom: 12), diff --git a/lib/src/screens/task_screen.dart b/lib/src/screens/task_screen.dart index 58c898a..42a2886 100644 --- a/lib/src/screens/task_screen.dart +++ b/lib/src/screens/task_screen.dart @@ -14,8 +14,15 @@ class TaskScreen extends StatefulWidget { /// Wether the Screen is going to edit a current task or create a new one. final bool isEdit; + /// Id of the task to edit if in edit mode. + final String taskId; + + /// Creates a screen capable of editing of creating a new task. + /// + /// [taskId] must be provided and cannot be null if [isEdit] is set to true. TaskScreen({ this.isEdit = false, + this.taskId, }); @override @@ -24,11 +31,14 @@ class TaskScreen extends StatefulWidget { class _TaskScreenState extends State { /// An instance of this screen's bloc. - final TaskBloc bloc = TaskBloc(); + TaskBloc bloc; initState() { if (widget.isEdit) { + bloc = TaskBloc(taskId: widget.taskId); bloc.populateWithCurrentTask(); + } else { + bloc = TaskBloc(); } super.initState(); } diff --git a/lib/src/services/current_selection_service.dart b/lib/src/services/current_selection_service.dart deleted file mode 100644 index 4a2ccff..0000000 --- a/lib/src/services/current_selection_service.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:rxdart/rxdart.dart'; - -import '../models/event_model.dart'; -import '../models/task_model.dart'; - -/// A service that keeps track of the current user selection. -/// -/// When editing a task or when navigating to an event screen, the new screens -/// can grab the user selection for this service. -class CurrentSelectionService { - /// The current selected task. - final _selectedTask = BehaviorSubject(); - - /// The current selected event. - final _selectedEvent = BehaviorSubject(); - - /// An observable of the current selected event. - Observable get task => _selectedTask.stream; - - /// The current selected event. - Observable get event => _selectedEvent.stream; - - /// Updates the current selected task. - Function(TaskModel) get updateSelectedTask => _selectedTask.sink.add; - - // Updates the current selected event. - Function(EventModel) get updateSelectedEvent => _selectedEvent.sink.add; - - dispose() { - _selectedEvent.close(); - _selectedTask.close(); - } -} - -final currentSelectionService = CurrentSelectionService();