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:rxdart/rxdart.dart';
|
||||
|
||||
import '../models/event_model.dart';
|
||||
import '../models/task_model.dart';
|
||||
import '../models/user_model.dart';
|
||||
import '../resources/firestore_provider.dart';
|
||||
import '../resources/firebase_storage_provider.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.
|
||||
class EventBloc {
|
||||
|
|
@ -27,23 +30,100 @@ class EventBloc {
|
|||
/// A subject of list of task model.
|
||||
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.
|
||||
/// An observable of the tasks linked to the event.
|
||||
Observable<List<TaskModel>> get eventTasks =>
|
||||
_tasks.stream.transform(kTaskListPriorityTransforemer);
|
||||
|
||||
Future<File> get testFile => _storage.getFile(
|
||||
'26LVkBPFkHVekVDatitVh6MXrm53/thumb@c64293a0-5970-11e9-d441-03e3ea627257.png');
|
||||
/// An observable of the list of paths of images linked to this event.
|
||||
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({
|
||||
@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
|
||||
/// selected event.
|
||||
Future<void> fetchTasks() async {
|
||||
final user = await _auth.currentUser;
|
||||
_firestore.getUserTasks(user.email, event: eventName).pipe(_tasks);
|
||||
await _ready;
|
||||
_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.
|
||||
|
|
@ -55,6 +135,16 @@ class EventBloc {
|
|||
}
|
||||
|
||||
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();
|
||||
_tasks.close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class HomeBloc {
|
|||
return tasks;
|
||||
},
|
||||
)
|
||||
.transform(searchBoxTransformer())
|
||||
.transform(_searchBoxTransformer())
|
||||
.transform(kTaskListPriorityTransforemer);
|
||||
|
||||
/// An observable of the current logged in user.
|
||||
|
|
@ -52,7 +52,7 @@ class HomeBloc {
|
|||
// TODO: Include the priority in the filtering.
|
||||
/// Returns a stream transformer that filters the task with the text from
|
||||
/// the search box.
|
||||
StreamTransformer<List<TaskModel>, List<TaskModel>> searchBoxTransformer() {
|
||||
StreamTransformer<List<TaskModel>, List<TaskModel>> _searchBoxTransformer() {
|
||||
return StreamTransformer.fromHandlers(
|
||||
handleData: (taskList, sink) {
|
||||
sink.add(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../utils.dart' show kBigTextStyle;
|
||||
import '../utils.dart' show kBlueGradient, getImageThumbnailPath;
|
||||
import '../blocs/event_bloc.dart';
|
||||
import '../models/task_model.dart';
|
||||
import '../widgets/custom_app_bar.dart';
|
||||
import '../widgets/fractionally_screen_sized_box.dart';
|
||||
import '../widgets/loading_indicator.dart';
|
||||
import '../widgets/task_list_tile.dart';
|
||||
|
||||
|
|
@ -28,6 +30,7 @@ class _EventScreenState extends State<EventScreen>
|
|||
super.initState();
|
||||
bloc = EventBloc(eventName: widget.eventName);
|
||||
bloc.fetchTasks();
|
||||
bloc.fetchImagesPaths();
|
||||
_tabController = TabController(vsync: this, length: 2);
|
||||
}
|
||||
|
||||
|
|
@ -93,21 +96,74 @@ class _EventScreenState extends State<EventScreen>
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: refactor this.
|
||||
Widget buildMediaView() {
|
||||
return Center(
|
||||
child: FutureBuilder(
|
||||
future: bloc.testFile,
|
||||
builder: (BuildContext context, AsyncSnapshot<File> snap) {
|
||||
if (!snap.hasData) {
|
||||
return StreamBuilder(
|
||||
stream: bloc.imagesPaths,
|
||||
builder: (BuildContext context, AsyncSnapshot<List<String>> listSnap) {
|
||||
if (!listSnap.hasData) {
|
||||
return Center(
|
||||
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() {
|
||||
bloc.dispose();
|
||||
|
|
|
|||
|
|
@ -60,3 +60,10 @@ final StreamTransformer<List<TaskModel>, List<TaskModel>>
|
|||
.compareTo(TaskModel.ecodedPriority(a.priority)));
|
||||
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