diff --git a/lib/src/blocs/event_bloc.dart b/lib/src/blocs/event_bloc.dart index 05d52c2..72e4665 100644 --- a/lib/src/blocs/event_bloc.dart +++ b/lib/src/blocs/event_bloc.dart @@ -1,19 +1,54 @@ import 'dart:async'; -import 'package:meta/meta.dart'; +import 'package:meta/meta.dart'; +import 'package:rxdart/rxdart.dart'; + +import '../models/task_model.dart'; import '../resources/firestore_provider.dart'; import '../resources/firebase_storage_provider.dart'; +import '../services/auth_service.dart'; +import '../utils.dart' show kTaskListPriorityTransforemer; /// A business logic component that manages the state for an event screen. class EventBloc { /// The name of the event being shown. final String eventName; + + /// An instance of a firestore provider. + final FirestoreProvider _firestore = firestoreProvider; + + /// An instace of the auth service. + final AuthService _auth = authService; + + /// A subject of list of task model. + final _tasks = BehaviorSubject>(); + + // Stream getters. + /// An observable of the tasks linked to the event. + Observable> get eventTasks => + _tasks.stream.transform(kTaskListPriorityTransforemer); + EventBloc({ @required this.eventName, }); /// Fetches the tasks for the current user that a part of the currently /// selected event. - void fetchTasks() {} - void dispose() {} + Future fetchTasks() async { + final user = await _auth.currentUser; + _firestore.getUserTasks(user.email, event: eventName).pipe(_tasks); + } + + /// Marks a task as done in the database. + void markTaskAsDone(TaskModel task) async { + _firestore.updateTask( + task.id, + done: true, + ); + } + + void dispose() async { + await _tasks.drain(); + _tasks.close(); + } } diff --git a/lib/src/blocs/home_bloc.dart b/lib/src/blocs/home_bloc.dart index a4a22e4..99b42a1 100644 --- a/lib/src/blocs/home_bloc.dart +++ b/lib/src/blocs/home_bloc.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:rxdart/rxdart.dart'; +import '../utils.dart' show kTaskListPriorityTransforemer; import '../models/task_model.dart'; import '../resources/firestore_provider.dart'; import '../services/auth_service.dart'; @@ -41,21 +42,13 @@ class HomeBloc { (text, tasks) { return tasks; }, - ).transform(searchBoxTransformer()).transform(prioritySortTransformer()); + ) + .transform(searchBoxTransformer()) + .transform(kTaskListPriorityTransforemer); /// An observable of the current logged in user. Observable get userStream => _auth.userStream; - /// Returns a stream transformer that sorts the tasks by priority. - StreamTransformer, List> - prioritySortTransformer() { - return StreamTransformer.fromHandlers(handleData: (tasksList, sink) { - tasksList.sort((a, b) => TaskModel.ecodedPriority(b.priority) - .compareTo(TaskModel.ecodedPriority(a.priority))); - sink.add(tasksList); - }); - } - // TODO: Include the priority in the filtering. /// Returns a stream transformer that filters the task with the text from /// the search box. diff --git a/lib/src/screens/event_screen.dart b/lib/src/screens/event_screen.dart index b8c2000..91104ca 100644 --- a/lib/src/screens/event_screen.dart +++ b/lib/src/screens/event_screen.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; -import '../utils.dart'; +import '../utils.dart' show kBigTextStyle; import '../blocs/event_bloc.dart'; +import '../models/task_model.dart'; import '../widgets/custom_app_bar.dart'; +import '../widgets/loading_indicator.dart'; +import '../widgets/task_list_tile.dart'; class EventScreen extends StatefulWidget { /// The name of the event this screenn is showing. @@ -22,6 +25,7 @@ class _EventScreenState extends State initState() { super.initState(); bloc = EventBloc(eventName: widget.eventName); + bloc.fetchTasks(); _tabController = TabController(vsync: this, length: 2); } @@ -31,12 +35,7 @@ class _EventScreenState extends State body: TabBarView( controller: _tabController, children: [ - Center( - child: Text( - 'Tasks', - style: kBigTextStyle, - ), - ), + buildTasksListView(), Center( child: Text( 'Media', @@ -65,6 +64,38 @@ class _EventScreenState extends State ); } + Widget buildTasksListView() { + return StreamBuilder( + stream: bloc.eventTasks, + builder: (BuildContext context, AsyncSnapshot> snap) { + if (!snap.hasData) { + return Center( + child: LoadingIndicator(), + ); + } + + return ListView( + padding: EdgeInsets.only(top: 15), + children: snap.data + .map((task) => Container( + child: TaskListTile( + task: task, + hideEventButton: true, + onDone: () => bloc.markTaskAsDone(task), + onEditPressed: () { + // Include the id of the task to be edited in the route. + Navigator.of(context).pushNamed('editTask/${task.id}'); + }, + ), + padding: EdgeInsets.only(bottom: 12), + )) + .toList() + ..add(Container(height: 70)), + ); + }, + ); + } + void dispose() { bloc.dispose(); super.dispose(); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 490d1c6..4ee4692 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -51,3 +51,12 @@ class Validators { }, ); } + +/// Returns a stream transformer that sorts tasks by priority. +final StreamTransformer, List> + kTaskListPriorityTransforemer = + StreamTransformer.fromHandlers(handleData: (tasksList, sink) { + tasksList.sort((a, b) => TaskModel.ecodedPriority(b.priority) + .compareTo(TaskModel.ecodedPriority(a.priority))); + sink.add(tasksList); +}); diff --git a/lib/src/widgets/custom_app_bar.dart b/lib/src/widgets/custom_app_bar.dart index e84395e..ccb2f7e 100644 --- a/lib/src/widgets/custom_app_bar.dart +++ b/lib/src/widgets/custom_app_bar.dart @@ -13,14 +13,20 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { final PreferredSizeWidget bottom; /// The size of only the app bar part. - final double appBarHeight; + /// + /// It will vary depending on the existance of the bottom widget. + final double _appBarHeight; + CustomAppBar({ this.title = '', this.bottom, - }) : appBarHeight = bottom == null ? 140.0 : 120.0; + }) : _appBarHeight = bottom == null ? 140.0 : 120.0; + /// The preferred size of the app bar. + /// + /// Consider the size of the bottom widget if there is one. Size get preferredSize => - Size.fromHeight(appBarHeight + (bottom?.preferredSize?.height ?? 0)); + Size.fromHeight(_appBarHeight + (bottom?.preferredSize?.height ?? 0)); Widget build(BuildContext context) { Widget result = Container( diff --git a/lib/src/widgets/task_list_tile.dart b/lib/src/widgets/task_list_tile.dart index 6d40653..4ca5717 100644 --- a/lib/src/widgets/task_list_tile.dart +++ b/lib/src/widgets/task_list_tile.dart @@ -19,6 +19,9 @@ class TaskListTile extends StatelessWidget { /// Function to be called when the "event" button is pressed. final VoidCallback onEventPressed; + /// Whether or not the event button should be hidden. + final bool hideEventButton; + /// Height of the priority badge. /// /// Also used to calculate the padding for the first section. @@ -33,6 +36,7 @@ class TaskListTile extends StatelessWidget { this.onDone, this.onEditPressed, this.onEventPressed, + this.hideEventButton = false, }) : assert(task != null); Widget build(BuildContext context) { @@ -96,6 +100,26 @@ class TaskListTile extends StatelessWidget { /// Builds the section that contains the 3 buttons for the tile. Widget buildButtonSection() { + final bottomRowChildren = []; + if (!hideEventButton) { + bottomRowChildren.addAll([ + ActionButton( + onPressed: onEditPressed, + text: 'Edit', + leadingIconData: Icons.edit, + ), + SizedBox( + width: 4, + ), + ]); + } + bottomRowChildren.add( + ActionButton( + onPressed: onEventPressed, + text: 'Event', + leadingIconData: FontAwesomeIcons.calendar, + ), + ); return Expanded( flex: 5, child: Column( @@ -113,21 +137,7 @@ class TaskListTile extends StatelessWidget { ), Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - ActionButton( - onPressed: onEditPressed, - text: 'Edit', - leadingIconData: Icons.edit, - ), - SizedBox( - width: 4, - ), - ActionButton( - onPressed: onEventPressed, - text: 'Event', - leadingIconData: FontAwesomeIcons.calendar, - ) - ], + children: bottomRowChildren, ), ], ),