Added preliminary code for the media screen
This commit is contained in:
parent
02d7dd5be3
commit
ec505eb27e
4 changed files with 173 additions and 20 deletions
|
|
@ -4,11 +4,14 @@ import 'dart:io';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
|
import '../models/event_model.dart';
|
||||||
import '../models/task_model.dart';
|
import '../models/task_model.dart';
|
||||||
|
import '../models/user_model.dart';
|
||||||
import '../resources/firestore_provider.dart';
|
import '../resources/firestore_provider.dart';
|
||||||
import '../resources/firebase_storage_provider.dart';
|
import '../resources/firebase_storage_provider.dart';
|
||||||
import '../services/auth_service.dart';
|
import '../services/auth_service.dart';
|
||||||
import '../utils.dart' show kTaskListPriorityTransforemer;
|
import '../utils.dart'
|
||||||
|
show kTaskListPriorityTransforemer, getImageThumbnailPath;
|
||||||
|
|
||||||
/// A business logic component that manages the state for an event screen.
|
/// A business logic component that manages the state for an event screen.
|
||||||
class EventBloc {
|
class EventBloc {
|
||||||
|
|
@ -27,23 +30,100 @@ class EventBloc {
|
||||||
/// A subject of list of task model.
|
/// A subject of list of task model.
|
||||||
final _tasks = BehaviorSubject<List<TaskModel>>();
|
final _tasks = BehaviorSubject<List<TaskModel>>();
|
||||||
|
|
||||||
|
/// A subject of the list of image paths for this event.
|
||||||
|
final _imagesPaths = BehaviorSubject<List<String>>();
|
||||||
|
|
||||||
|
/// A subject of String paths.
|
||||||
|
final _imagesFetcher = PublishSubject<String>();
|
||||||
|
|
||||||
|
/// A subject of String paths.
|
||||||
|
final _imagesThumbnailsFetcher = PublishSubject<String>();
|
||||||
|
|
||||||
|
/// A subject of a cache that contains the image files.
|
||||||
|
final _thumbnails = BehaviorSubject<Map<String, Future<File>>>();
|
||||||
|
|
||||||
|
/// A subject of a cache that contains the image files.
|
||||||
|
final _images = BehaviorSubject<Map<String, Future<File>>>();
|
||||||
|
|
||||||
|
/// The event being managed by this bloc.
|
||||||
|
EventModel _event;
|
||||||
|
|
||||||
|
/// The representation of the current signed in user.
|
||||||
|
UserModel _user;
|
||||||
|
|
||||||
|
/// Whether the event and user models have been fetched;
|
||||||
|
Future<void> _ready;
|
||||||
// Stream getters.
|
// Stream getters.
|
||||||
/// An observable of the tasks linked to the event.
|
/// An observable of the tasks linked to the event.
|
||||||
Observable<List<TaskModel>> get eventTasks =>
|
Observable<List<TaskModel>> get eventTasks =>
|
||||||
_tasks.stream.transform(kTaskListPriorityTransforemer);
|
_tasks.stream.transform(kTaskListPriorityTransforemer);
|
||||||
|
|
||||||
Future<File> get testFile => _storage.getFile(
|
/// An observable of the list of paths of images linked to this event.
|
||||||
'26LVkBPFkHVekVDatitVh6MXrm53/thumb@c64293a0-5970-11e9-d441-03e3ea627257.png');
|
Observable<List<String>> get imagesPaths => _imagesPaths.stream;
|
||||||
|
|
||||||
|
/// An observable of a cache of the images thumbnails files.
|
||||||
|
Observable<Map<String, Future<File>>> get thumbnails => _thumbnails.stream;
|
||||||
|
|
||||||
|
/// An observable of a cache of the images files.
|
||||||
|
Observable<Map<String, Future<File>>> get images => _images.stream;
|
||||||
|
|
||||||
|
// Sinks getters.
|
||||||
|
/// Starts the fetching process for an image given its path.
|
||||||
|
Function(String) get fetchImage => _imagesFetcher.sink.add;
|
||||||
|
|
||||||
|
/// Starts the fetching process for an image thumbail given its path.
|
||||||
|
Function(String) get fetchThumbnail => _imagesThumbnailsFetcher.sink.add;
|
||||||
|
|
||||||
EventBloc({
|
EventBloc({
|
||||||
@required this.eventName,
|
@required this.eventName,
|
||||||
});
|
}) {
|
||||||
|
_ready = _initUserAndEvent();
|
||||||
|
_imagesFetcher.transform(_imagesTransformer()).pipe(_images);
|
||||||
|
_imagesThumbnailsFetcher
|
||||||
|
.transform(_imagesTransformer(isThumbnail: true))
|
||||||
|
.pipe(_thumbnails);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the value for the User and the Event models.
|
||||||
|
Future<void> _initUserAndEvent() async {
|
||||||
|
final userModelFuture = _auth.getCurrentUserModel();
|
||||||
|
_user = await userModelFuture;
|
||||||
|
_event = await _firestore.getEvent(
|
||||||
|
(await userModelFuture).id,
|
||||||
|
eventName: eventName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a stream transformer that creates a cache map from image storage
|
||||||
|
/// bucket paths.
|
||||||
|
ScanStreamTransformer<String, Map<String, Future<File>>> _imagesTransformer({
|
||||||
|
bool isThumbnail = false,
|
||||||
|
}) {
|
||||||
|
final accumulator = (Map<String, Future<File>> cache, String path, _) {
|
||||||
|
if (isThumbnail) {
|
||||||
|
path = getImageThumbnailPath(path);
|
||||||
|
}
|
||||||
|
if (cache.containsKey(path)) {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
cache[path] = _storage.getFile(path);
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
return ScanStreamTransformer(accumulator, <String, Future<File>>{});
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetches the tasks for the current user that a part of the currently
|
/// Fetches the tasks for the current user that a part of the currently
|
||||||
/// selected event.
|
/// selected event.
|
||||||
Future<void> fetchTasks() async {
|
Future<void> fetchTasks() async {
|
||||||
final user = await _auth.currentUser;
|
await _ready;
|
||||||
_firestore.getUserTasks(user.email, event: eventName).pipe(_tasks);
|
_firestore.getUserTasks(_user.username, event: eventName).pipe(_tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetches the paths of all the images linked to this event.
|
||||||
|
Future<void> fetchImagesPaths() async {
|
||||||
|
await _ready;
|
||||||
|
_imagesPaths.sink.add(_event.media);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks a task as done in the database.
|
/// Marks a task as done in the database.
|
||||||
|
|
@ -55,6 +135,16 @@ class EventBloc {
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() async {
|
void dispose() async {
|
||||||
|
await _imagesThumbnailsFetcher.drain();
|
||||||
|
_imagesThumbnailsFetcher.close();
|
||||||
|
await _imagesFetcher.drain();
|
||||||
|
_imagesFetcher.close();
|
||||||
|
await _thumbnails.drain();
|
||||||
|
_thumbnails.close();
|
||||||
|
await _images.drain();
|
||||||
|
_images.close();
|
||||||
|
await _imagesPaths.drain();
|
||||||
|
_imagesPaths.close();
|
||||||
await _tasks.drain();
|
await _tasks.drain();
|
||||||
_tasks.close();
|
_tasks.close();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class HomeBloc {
|
||||||
return tasks;
|
return tasks;
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.transform(searchBoxTransformer())
|
.transform(_searchBoxTransformer())
|
||||||
.transform(kTaskListPriorityTransforemer);
|
.transform(kTaskListPriorityTransforemer);
|
||||||
|
|
||||||
/// An observable of the current logged in user.
|
/// An observable of the current logged in user.
|
||||||
|
|
@ -52,7 +52,7 @@ class HomeBloc {
|
||||||
// TODO: Include the priority in the filtering.
|
// TODO: Include the priority in the filtering.
|
||||||
/// Returns a stream transformer that filters the task with the text from
|
/// Returns a stream transformer that filters the task with the text from
|
||||||
/// the search box.
|
/// the search box.
|
||||||
StreamTransformer<List<TaskModel>, List<TaskModel>> searchBoxTransformer() {
|
StreamTransformer<List<TaskModel>, List<TaskModel>> _searchBoxTransformer() {
|
||||||
return StreamTransformer.fromHandlers(
|
return StreamTransformer.fromHandlers(
|
||||||
handleData: (taskList, sink) {
|
handleData: (taskList, sink) {
|
||||||
sink.add(
|
sink.add(
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../utils.dart' show kBigTextStyle;
|
import '../utils.dart' show kBlueGradient, getImageThumbnailPath;
|
||||||
import '../blocs/event_bloc.dart';
|
import '../blocs/event_bloc.dart';
|
||||||
import '../models/task_model.dart';
|
import '../models/task_model.dart';
|
||||||
import '../widgets/custom_app_bar.dart';
|
import '../widgets/custom_app_bar.dart';
|
||||||
|
import '../widgets/fractionally_screen_sized_box.dart';
|
||||||
import '../widgets/loading_indicator.dart';
|
import '../widgets/loading_indicator.dart';
|
||||||
import '../widgets/task_list_tile.dart';
|
import '../widgets/task_list_tile.dart';
|
||||||
|
|
||||||
|
|
@ -28,6 +30,7 @@ class _EventScreenState extends State<EventScreen>
|
||||||
super.initState();
|
super.initState();
|
||||||
bloc = EventBloc(eventName: widget.eventName);
|
bloc = EventBloc(eventName: widget.eventName);
|
||||||
bloc.fetchTasks();
|
bloc.fetchTasks();
|
||||||
|
bloc.fetchImagesPaths();
|
||||||
_tabController = TabController(vsync: this, length: 2);
|
_tabController = TabController(vsync: this, length: 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,21 +96,74 @@ class _EventScreenState extends State<EventScreen>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: refactor this.
|
||||||
Widget buildMediaView() {
|
Widget buildMediaView() {
|
||||||
return Center(
|
return StreamBuilder(
|
||||||
child: FutureBuilder(
|
stream: bloc.imagesPaths,
|
||||||
future: bloc.testFile,
|
builder: (BuildContext context, AsyncSnapshot<List<String>> listSnap) {
|
||||||
builder: (BuildContext context, AsyncSnapshot<File> snap) {
|
if (!listSnap.hasData) {
|
||||||
if (!snap.hasData) {
|
|
||||||
return Center(
|
return Center(
|
||||||
child: LoadingIndicator(),
|
child: LoadingIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Image.file(snap.data);
|
|
||||||
},
|
return GridView.builder(
|
||||||
|
itemCount: listSnap.data.length + 1,
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
gradient: kBlueGradient,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
final imagePath = listSnap.data[index - 1];
|
||||||
|
bloc.fetchThumbnail(imagePath);
|
||||||
|
return StreamBuilder(
|
||||||
|
stream: bloc.thumbnails,
|
||||||
|
builder: (BuildContext context,
|
||||||
|
AsyncSnapshot<Map<String, Future<File>>> thumbailsCacheSnap) {
|
||||||
|
if (!thumbailsCacheSnap.hasData) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return FutureBuilder(
|
||||||
|
future:
|
||||||
|
thumbailsCacheSnap.data[getImageThumbnailPath(imagePath)],
|
||||||
|
builder: (BuildContext context,
|
||||||
|
AsyncSnapshot<File> thumbnailFileSnap) {
|
||||||
|
if (!thumbnailFileSnap.hasData) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
child: Image.file(thumbnailFileSnap.data),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 3,
|
||||||
|
crossAxisSpacing: 5,
|
||||||
|
mainAxisSpacing: 5,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
bloc.dispose();
|
bloc.dispose();
|
||||||
|
|
|
||||||
|
|
@ -60,3 +60,10 @@ final StreamTransformer<List<TaskModel>, List<TaskModel>>
|
||||||
.compareTo(TaskModel.ecodedPriority(a.priority)));
|
.compareTo(TaskModel.ecodedPriority(a.priority)));
|
||||||
sink.add(tasksList);
|
sink.add(tasksList);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Gets the path of an image thumbnail from its original path.
|
||||||
|
String getImageThumbnailPath(String path) {
|
||||||
|
List<String> tokens = path.split('/');
|
||||||
|
tokens.last = 'thumb@' + tokens.last;
|
||||||
|
return tokens.join('/');
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue