Refactored the [CurrentSelectionService] to use streams
This commit is contained in:
parent
7abee74b73
commit
3b9f702a37
10 changed files with 136 additions and 43 deletions
|
|
@ -2,5 +2,20 @@ import 'dart:async';
|
|||
|
||||
import '../resources/firestore_provider.dart';
|
||||
import '../resources/firebase_storage_provider.dart';
|
||||
import '../services/current_selection_service.dart';
|
||||
|
||||
class EventBloc {}
|
||||
/// 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;
|
||||
|
||||
/// Fetches the tasks for the current user that a part of the currently
|
||||
/// selected event.
|
||||
void fetchTasks() {}
|
||||
void dispose() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class HomeBloc {
|
|||
final AuthService _auth = authService;
|
||||
|
||||
/// An instance of the firebase repository.
|
||||
final FirestoreProvider _repository = firestoreProvider;
|
||||
final FirestoreProvider _firestore = firestoreProvider;
|
||||
|
||||
/// An instance of the current task service.
|
||||
final CurrentSelectionService _selectionService = currentSelectionService;
|
||||
|
|
@ -90,7 +90,7 @@ class HomeBloc {
|
|||
/// Fetches the tasks for the current user.
|
||||
Future<void> fetchTasks() async {
|
||||
final user = await _auth.currentUser;
|
||||
_repository.getUserTasks(user.email).pipe(_tasks);
|
||||
_firestore.getUserTasks(user.email).pipe(_tasks);
|
||||
}
|
||||
|
||||
/// Returns a future of the avatar url for the current user.
|
||||
|
|
@ -107,17 +107,24 @@ class HomeBloc {
|
|||
|
||||
/// Marks a task as done in the database.
|
||||
void markTaskAsDone(TaskModel task) async {
|
||||
_repository.updateTask(
|
||||
_firestore.updateTask(
|
||||
task.id,
|
||||
done: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Sets the global current task.
|
||||
void updateCurrentTask(TaskModel task) {
|
||||
/// 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, eventId: task.event);
|
||||
_selectionService.updateSelectedEvent(event);
|
||||
}
|
||||
|
||||
/// Updates the serach box text.
|
||||
void updateSearchBoxText(String newText) {
|
||||
_searchBoxText.add(newText);
|
||||
|
|
|
|||
|
|
@ -29,11 +29,16 @@ class TaskBloc extends Object with Validators {
|
|||
/// A subject of task text.
|
||||
final _taskText = BehaviorSubject<String>();
|
||||
|
||||
/// A subject of the text of the current global task.
|
||||
final _textInitialValue = BehaviorSubject<String>();
|
||||
|
||||
/// The priority of the current task.
|
||||
TaskPriority priority = TaskPriority.high;
|
||||
|
||||
/// The text of the current global task.
|
||||
String get textInitialValue => _selectionService.task.text;
|
||||
/// The id of the current task.
|
||||
///
|
||||
/// Only to be used if editing an existing task.
|
||||
String taskId;
|
||||
|
||||
//Stream getters.
|
||||
/// An observable of the current user model.
|
||||
|
|
@ -50,6 +55,9 @@ class TaskBloc extends Object with Validators {
|
|||
Observable<bool> get submitEnabled =>
|
||||
Observable.combineLatest2(eventName, taskText, (a, b) => true);
|
||||
|
||||
/// An observable of the text of the global selected task.
|
||||
Observable<String> get textInitialvalue => _textInitialValue.stream;
|
||||
|
||||
//Sinks getters.
|
||||
/// Changes the current task event name.
|
||||
Function(String) get changeEventName => _eventName.sink.add;
|
||||
|
|
@ -67,12 +75,13 @@ 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<void> submit(isEdit) {
|
||||
if (isEdit) {
|
||||
return _firestore.updateTask(
|
||||
_selectionService.task.id,
|
||||
taskId,
|
||||
text: _taskText.value,
|
||||
priority: TaskModel.ecodedPriority(priority),
|
||||
);
|
||||
|
|
@ -97,14 +106,20 @@ class TaskBloc extends Object with Validators {
|
|||
/// Grabs the data from the current global task and pipes it to the local
|
||||
/// streams.
|
||||
void populateWithCurrentTask() {
|
||||
TaskModel currentTask = _selectionService.task;
|
||||
if (currentTask != null) {
|
||||
changeEventName(currentTask.event);
|
||||
changeTaskText(currentTask.text);
|
||||
}
|
||||
// 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 dispose() {
|
||||
_textInitialValue.close();
|
||||
_taskText.close();
|
||||
_user.close();
|
||||
_eventName.close();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,24 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class EventScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {}
|
||||
import '../blocs/event_bloc.dart';
|
||||
import '../widgets/custom_app_bar.dart';
|
||||
|
||||
class EventScreen extends StatefulWidget {
|
||||
_EventScreenState createState() => _EventScreenState();
|
||||
}
|
||||
|
||||
class _EventScreenState extends State<EventScreen> {
|
||||
final EventBloc bloc = EventBloc();
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: '',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
bloc.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,8 +92,11 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
child: TaskListTile(
|
||||
task: task,
|
||||
onDone: () => bloc.markTaskAsDone(task),
|
||||
onEdit: () {
|
||||
bloc.updateCurrentTask(task);
|
||||
onEventPressed: () {
|
||||
bloc.updateSelectedEvent(task);
|
||||
},
|
||||
onEditPressed: () {
|
||||
bloc.updateSelectedTask(task);
|
||||
Navigator.of(context).pushNamed('editTask/');
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -26,16 +26,10 @@ class _TaskScreenState extends State<TaskScreen> {
|
|||
/// An instance of this screen's bloc.
|
||||
final TaskBloc bloc = TaskBloc();
|
||||
|
||||
/// The initial value for the text field.
|
||||
///
|
||||
/// This only gets used whent the screen is set to edit.
|
||||
String textFieldInitialValue;
|
||||
|
||||
initState() {
|
||||
if (widget.isEdit) {
|
||||
bloc.populateWithCurrentTask();
|
||||
}
|
||||
textFieldInitialValue = bloc.textInitialValue;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
@ -50,11 +44,21 @@ class _TaskScreenState extends State<TaskScreen> {
|
|||
padding: const EdgeInsets.only(top: 15.0, left: 20.0, right: 20.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
BigTextInput(
|
||||
initialValue: widget.isEdit ? textFieldInitialValue : '',
|
||||
height: 95,
|
||||
onChanged: bloc.changeTaskText,
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: bloc.textInitialvalue,
|
||||
builder:
|
||||
(BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||
String textFieldInitialValue = '';
|
||||
if (snapshot.hasData) {
|
||||
textFieldInitialValue = snapshot.data;
|
||||
}
|
||||
return BigTextInput(
|
||||
initialValue:
|
||||
widget.isEdit ? textFieldInitialValue : '',
|
||||
height: 95,
|
||||
onChanged: bloc.changeTaskText,
|
||||
);
|
||||
}),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -63,6 +63,15 @@ class AuthService {
|
|||
return _googleSignInProvider.signOut();
|
||||
}
|
||||
|
||||
/// Returns the model that represents the current logged in user.
|
||||
Future<UserModel> getCurrentUserModel() async {
|
||||
final user = await _googleSignInProvider.getCurrentUser();
|
||||
if (user != null) {
|
||||
return _firestoreProvider.getUser(username: user.email);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_user.close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
import '../models/event_model.dart';
|
||||
import '../models/task_model.dart';
|
||||
|
||||
|
|
@ -7,25 +9,26 @@ import '../models/task_model.dart';
|
|||
/// can grab the user selection for this service.
|
||||
class CurrentSelectionService {
|
||||
/// The current selected task.
|
||||
TaskModel _selectedTask;
|
||||
final _selectedTask = BehaviorSubject<TaskModel>();
|
||||
|
||||
/// The current selected event.
|
||||
EventModel _selectedEvent;
|
||||
final _selectedEvent = BehaviorSubject<EventModel>();
|
||||
|
||||
/// The current selected task.
|
||||
TaskModel get task => _selectedTask;
|
||||
/// An observable of the current selected event.
|
||||
Observable<TaskModel> get task => _selectedTask.stream;
|
||||
|
||||
/// The current selected event.
|
||||
EventModel get event => _selectedEvent;
|
||||
Observable<EventModel> get event => _selectedEvent.stream;
|
||||
|
||||
/// Updates the current selected task.
|
||||
void updateSelectedTask(TaskModel newTask) {
|
||||
_selectedTask = newTask;
|
||||
}
|
||||
Function(TaskModel) get updateSelectedTask => _selectedTask.sink.add;
|
||||
|
||||
// Updates the current selected event.
|
||||
void updateSelectedEvent(EventModel newEvent) {
|
||||
_selectedEvent = newEvent;
|
||||
Function(EventModel) get updateSelectedEvent => _selectedEvent.sink.add;
|
||||
|
||||
dispose() {
|
||||
_selectedEvent.close();
|
||||
_selectedTask.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class BigTextInput extends StatefulWidget {
|
|||
this.height,
|
||||
this.width,
|
||||
this.elevated = true,
|
||||
this.initialValue,
|
||||
this.initialValue = '',
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -33,6 +33,13 @@ class _BigTextInputState extends State<BigTextInput> {
|
|||
/// Custom controller for the text input.
|
||||
TextEditingController _controller;
|
||||
|
||||
/// Flag to indicate if the initial value has been set or not.
|
||||
///
|
||||
/// Without this flag the text property of the controller will be set
|
||||
/// continuously and the cursor inside the text field will return to the
|
||||
/// origin when tapped.
|
||||
bool initialValueWasSet = false;
|
||||
|
||||
/// Setus up the controller.
|
||||
void initState() {
|
||||
_controller = TextEditingController(text: widget.initialValue);
|
||||
|
|
@ -40,6 +47,17 @@ class _BigTextInputState extends State<BigTextInput> {
|
|||
super.initState();
|
||||
}
|
||||
|
||||
// Set the text property of the controller if the newly rendered widget
|
||||
// contains a non empty initial value. It should only be done once, otherwise
|
||||
// the cursor will continuously return to the start of the input when tapped.
|
||||
void didUpdateWidget(oldWidget) {
|
||||
if (widget.initialValue != '' && !initialValueWasSet) {
|
||||
_controller.text = widget.initialValue;
|
||||
initialValueWasSet = true;
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
/// Calls [onChanged] when updates are sent by the controller.
|
||||
void controllerListener() {
|
||||
widget.onChanged(_controller.text);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class TaskListTile extends StatelessWidget {
|
|||
final VoidCallback onDone;
|
||||
|
||||
/// Function to be called when the "edit" button is pressed.
|
||||
final VoidCallback onEdit;
|
||||
final VoidCallback onEditPressed;
|
||||
|
||||
/// Function to be called when the "event" button is pressed.
|
||||
final VoidCallback onEventPressed;
|
||||
|
|
@ -31,7 +31,7 @@ class TaskListTile extends StatelessWidget {
|
|||
TaskListTile({
|
||||
@required this.task,
|
||||
this.onDone,
|
||||
this.onEdit,
|
||||
this.onEditPressed,
|
||||
this.onEventPressed,
|
||||
}) : assert(task != null);
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ class TaskListTile extends StatelessWidget {
|
|||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
ActionButton(
|
||||
onPressed: onEdit,
|
||||
onPressed: onEditPressed,
|
||||
text: 'Edit',
|
||||
leadingIconData: Icons.edit,
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue