feat: combine UI screens with service layer

This commit is contained in:
Freek van de Ven 2023-11-20 11:54:00 +01:00
parent 4113e9fea2
commit e8822d92a3
8 changed files with 97 additions and 33 deletions

View file

@ -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';

View file

@ -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<TimelinePost> _posts = [];
@override
Future<void> createPost(TimelinePost post) async {
var imageRef = _storage.ref().child('timeline/${post.id}');
Future<TimelinePost> 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<List<TimelinePost>> fetchPosts(String? category) async {
var snapshot = await _db
var snapshot = (category != null)
? await _db
.collection(_options.timelineCollectionName)
.where('category', isEqualTo: category)
.get();
.get()
: await _db.collection(_options.timelineCollectionName).get();
var posts = <TimelinePost>[];
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<TimelinePost> getPosts(String? category) => _posts
.where((element) => category == null || element.category == category)
.toList();
@override
Future<void> likePost(String userId, TimelinePost post) {
// update the post with the new like

View file

@ -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 {

View file

@ -9,8 +9,9 @@ import 'package:flutter_timeline_interface/src/model/timeline_reaction.dart';
abstract class TimelineService {
Future<void> deletePost(TimelinePost post);
Future<void> createPost(TimelinePost post);
Future<TimelinePost> createPost(TimelinePost post);
Future<List<TimelinePost>> fetchPosts(String? category);
List<TimelinePost> getPosts(String? category);
Future<TimelinePost> fetchPostDetails(TimelinePost post);
Future<void> reactToPost(
TimelinePost post,

View file

@ -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<void> 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,

View file

@ -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,10 +50,15 @@ class _TimelineScreenState extends State<TimelineScreen> {
}
@override
Widget build(BuildContext context) => SingleChildScrollView(
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 widget.posts)
for (var post in snapshot.data!)
if (widget.timelineCategoryFilter == null ||
post.category == widget.timelineCategoryFilter)
Padding(
@ -62,4 +73,9 @@ class _TimelineScreenState extends State<TimelineScreen> {
],
),
);
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
}