From e8822d92a39beab371dd6a9942af58727eb79a64 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Mon, 20 Nov 2023 11:54:00 +0100 Subject: [PATCH] feat: combine UI screens with service layer --- .../lib/flutter_timeline_firebase.dart | 4 ++ .../config/firebase_timeline_options.dart | 0 .../models/firebase_user_document.dart | 0 .../service/firebase_timeline_service.dart | 33 ++++++++---- .../service/firebase_user_service.dart | 4 +- .../lib/src/services/timeline_service.dart | 3 +- .../timeline_post_creation_screen.dart | 36 ++++++++++++- .../lib/src/screens/timeline_screen.dart | 50 ++++++++++++------- 8 files changed, 97 insertions(+), 33 deletions(-) rename packages/flutter_timeline_firebase/lib/{ => src}/config/firebase_timeline_options.dart (100%) rename packages/flutter_timeline_firebase/lib/{ => src}/models/firebase_user_document.dart (100%) rename packages/flutter_timeline_firebase/lib/{ => src}/service/firebase_timeline_service.dart (78%) rename packages/flutter_timeline_firebase/lib/{ => src}/service/firebase_user_service.dart (90%) diff --git a/packages/flutter_timeline_firebase/lib/flutter_timeline_firebase.dart b/packages/flutter_timeline_firebase/lib/flutter_timeline_firebase.dart index 34125a0..9ad1f86 100644 --- a/packages/flutter_timeline_firebase/lib/flutter_timeline_firebase.dart +++ b/packages/flutter_timeline_firebase/lib/flutter_timeline_firebase.dart @@ -4,3 +4,7 @@ /// library flutter_timeline_firebase; + +export 'src/config/firebase_timeline_options.dart'; +export 'src/service/firebase_timeline_service.dart'; +export 'src/service/firebase_user_service.dart'; diff --git a/packages/flutter_timeline_firebase/lib/config/firebase_timeline_options.dart b/packages/flutter_timeline_firebase/lib/src/config/firebase_timeline_options.dart similarity index 100% rename from packages/flutter_timeline_firebase/lib/config/firebase_timeline_options.dart rename to packages/flutter_timeline_firebase/lib/src/config/firebase_timeline_options.dart diff --git a/packages/flutter_timeline_firebase/lib/models/firebase_user_document.dart b/packages/flutter_timeline_firebase/lib/src/models/firebase_user_document.dart similarity index 100% rename from packages/flutter_timeline_firebase/lib/models/firebase_user_document.dart rename to packages/flutter_timeline_firebase/lib/src/models/firebase_user_document.dart diff --git a/packages/flutter_timeline_firebase/lib/service/firebase_timeline_service.dart b/packages/flutter_timeline_firebase/lib/src/service/firebase_timeline_service.dart similarity index 78% rename from packages/flutter_timeline_firebase/lib/service/firebase_timeline_service.dart rename to packages/flutter_timeline_firebase/lib/src/service/firebase_timeline_service.dart index c59727d..3048832 100644 --- a/packages/flutter_timeline_firebase/lib/service/firebase_timeline_service.dart +++ b/packages/flutter_timeline_firebase/lib/src/service/firebase_timeline_service.dart @@ -8,8 +8,9 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_timeline_firebase/config/firebase_timeline_options.dart'; +import 'package:flutter_timeline_firebase/src/config/firebase_timeline_options.dart'; import 'package:flutter_timeline_interface/flutter_timeline_interface.dart'; +import 'package:uuid/uuid.dart'; class FirebaseTimelineService implements TimelineService { FirebaseTimelineService({ @@ -32,14 +33,17 @@ class FirebaseTimelineService implements TimelineService { List _posts = []; @override - Future createPost(TimelinePost post) async { - var imageRef = _storage.ref().child('timeline/${post.id}'); + Future createPost(TimelinePost post) async { + var postId = const Uuid().v4(); + var imageRef = _storage.ref().child('timeline/$postId'); var result = await imageRef.putData(post.image!); var imageUrl = await result.ref.getDownloadURL(); - var updatedPost = post.copyWith(imageUrl: imageUrl); - var postRef = _db.collection(_options.timelineCollectionName).doc(post.id); + var updatedPost = post.copyWith(imageUrl: imageUrl, id: postId); + var postRef = + _db.collection(_options.timelineCollectionName).doc(updatedPost.id); _posts.add(updatedPost); - return postRef.set(updatedPost.toJson()); + await postRef.set(updatedPost.toJson()); + return updatedPost; } @override @@ -56,15 +60,17 @@ class FirebaseTimelineService implements TimelineService { @override Future> fetchPosts(String? category) async { - var snapshot = await _db - .collection(_options.timelineCollectionName) - .where('category', isEqualTo: category) - .get(); + var snapshot = (category != null) + ? await _db + .collection(_options.timelineCollectionName) + .where('category', isEqualTo: category) + .get() + : await _db.collection(_options.timelineCollectionName).get(); var posts = []; for (var doc in snapshot.docs) { var data = doc.data(); - var user = await _userService.getUser(data['user_id']); + var user = await _userService.getUser(data['creator_id']); var post = TimelinePost.fromJson(doc.id, data).copyWith(creator: user); posts.add(post); } @@ -72,6 +78,11 @@ class FirebaseTimelineService implements TimelineService { return posts; } + @override + List getPosts(String? category) => _posts + .where((element) => category == null || element.category == category) + .toList(); + @override Future likePost(String userId, TimelinePost post) { // update the post with the new like diff --git a/packages/flutter_timeline_firebase/lib/service/firebase_user_service.dart b/packages/flutter_timeline_firebase/lib/src/service/firebase_user_service.dart similarity index 90% rename from packages/flutter_timeline_firebase/lib/service/firebase_user_service.dart rename to packages/flutter_timeline_firebase/lib/src/service/firebase_user_service.dart index cb09c62..fb1da17 100644 --- a/packages/flutter_timeline_firebase/lib/service/firebase_user_service.dart +++ b/packages/flutter_timeline_firebase/lib/src/service/firebase_user_service.dart @@ -4,8 +4,8 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; -import 'package:flutter_timeline_firebase/config/firebase_timeline_options.dart'; -import 'package:flutter_timeline_firebase/models/firebase_user_document.dart'; +import 'package:flutter_timeline_firebase/src/config/firebase_timeline_options.dart'; +import 'package:flutter_timeline_firebase/src/models/firebase_user_document.dart'; import 'package:flutter_timeline_interface/flutter_timeline_interface.dart'; class FirebaseUserService implements TimelineUserService { diff --git a/packages/flutter_timeline_interface/lib/src/services/timeline_service.dart b/packages/flutter_timeline_interface/lib/src/services/timeline_service.dart index 87c459e..d00c911 100644 --- a/packages/flutter_timeline_interface/lib/src/services/timeline_service.dart +++ b/packages/flutter_timeline_interface/lib/src/services/timeline_service.dart @@ -9,8 +9,9 @@ import 'package:flutter_timeline_interface/src/model/timeline_reaction.dart'; abstract class TimelineService { Future deletePost(TimelinePost post); - Future createPost(TimelinePost post); + Future createPost(TimelinePost post); Future> fetchPosts(String? category); + List getPosts(String? category); Future fetchPostDetails(TimelinePost post); Future reactToPost( TimelinePost post, diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart index b4a88ea..44a2c71 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart @@ -7,15 +7,30 @@ import 'dart:typed_data'; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/material.dart'; import 'package:flutter_image_picker/flutter_image_picker.dart'; +import 'package:flutter_timeline_interface/flutter_timeline_interface.dart'; import 'package:flutter_timeline_view/src/config/timeline_options.dart'; class TimelinePostCreationScreen extends StatefulWidget { const TimelinePostCreationScreen({ + required this.userId, + required this.postCategory, + required this.onPostCreated, + required this.service, required this.options, this.padding = const EdgeInsets.symmetric(vertical: 24, horizontal: 16), super.key, }); + final String userId; + + final String postCategory; + + /// called when the post is created + final Function(TimelinePost) onPostCreated; + + /// The service to use for creating the post + final TimelineService service; + /// The options for the timeline final TimelineOptions options; @@ -59,6 +74,23 @@ class _TimelinePostCreationScreenState @override Widget build(BuildContext context) { + Future onPostCreated() async { + var post = TimelinePost( + id: '', + creatorId: widget.userId, + title: titleController.text, + category: widget.postCategory, + content: contentController.text, + likes: 0, + reaction: 0, + createdAt: DateTime.now(), + reactionEnabled: allowComments, + image: image, + ); + var newPost = await widget.service.createPost(post); + widget.onPostCreated.call(newPost); + } + var theme = Theme.of(context); return Padding( padding: widget.padding, @@ -214,12 +246,12 @@ class _TimelinePostCreationScreenState child: (widget.options.buttonBuilder != null) ? widget.options.buttonBuilder!( context, - () {}, + onPostCreated, widget.options.translations.checkPost, enabled: editingDone, ) : ElevatedButton( - onPressed: editingDone ? () {} : null, + onPressed: editingDone ? onPostCreated : null, child: Text( widget.options.translations.checkPost, style: theme.textTheme.bodyMedium, diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart index d0be49b..3282979 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -9,15 +9,21 @@ import 'package:flutter_timeline_view/src/widgets/timeline_post_widget.dart'; class TimelineScreen extends StatefulWidget { const TimelineScreen({ + required this.userId, required this.options, required this.posts, required this.onPostTap, + required this.service, this.controller, this.timelineCategoryFilter, this.timelinePostHeight = 100.0, super.key, }); + final String userId; + + final TimelineService service; + final TimelineOptions options; final ScrollController? controller; @@ -44,22 +50,32 @@ class _TimelineScreenState extends State { } @override - Widget build(BuildContext context) => SingleChildScrollView( - child: Column( - children: [ - for (var post in widget.posts) - if (widget.timelineCategoryFilter == null || - post.category == widget.timelineCategoryFilter) - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: TimelinePostWidget( - options: widget.options, - post: post, - height: widget.timelinePostHeight, - onTap: () => widget.onPostTap.call(post), - ), - ), - ], - ), + Widget build(BuildContext context) => FutureBuilder( + // ignore: discarded_futures + future: widget.service.fetchPosts(widget.timelineCategoryFilter), + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data != null) { + return SingleChildScrollView( + child: Column( + children: [ + for (var post in snapshot.data!) + if (widget.timelineCategoryFilter == null || + post.category == widget.timelineCategoryFilter) + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: TimelinePostWidget( + options: widget.options, + post: post, + height: widget.timelinePostHeight, + onTap: () => widget.onPostTap.call(post), + ), + ), + ], + ), + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, ); }