Skip to content

Commit

Permalink
Merge pull request #13 from SimCoderYoutube/lesson_13/retweet_a_post_…
Browse files Browse the repository at this point in the history
…with_firebase_firestore

[Add] retweet action
  • Loading branch information
SimCoderYoutube committed May 18, 2021
2 parents 14f1a5b + 36ce9b7 commit 4166c50
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 68 deletions.
23 changes: 23 additions & 0 deletions backend/functions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,26 @@ exports.deleteLike = functions.firestore.document('/posts/{postId}/likes/{userId
likesCount: admin.firestore.FieldValue.increment(-1)
})
})


exports.addRetweet = functions.firestore.document('/posts/{postId}/retweets/{userId}')
.onCreate((snap, context) => {
return db
.collection("posts")
.doc(context.params.postId)
.update(
{
retweetsCount: admin.firestore.FieldValue.increment(1)
})
})

exports.deleteRetweet = functions.firestore.document('/posts/{postId}/retweets/{userId}')
.onDelete((snap, context) => {
return db
.collection("posts")
.doc(context.params.postId)
.update(
{
retweetsCount: admin.firestore.FieldValue.increment(-1)
})
})
13 changes: 12 additions & 1 deletion frontend/lib/models/post.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,19 @@ class PostModel {
final String creator;
final String text;
final Timestamp timestamp;
final String originalId;
final bool retweet;

int likesCount;
int retweetsCount;

PostModel(
{this.id, this.creator, this.text, this.timestamp, this.likesCount});
{this.id,
this.creator,
this.text,
this.timestamp,
this.likesCount,
this.retweetsCount,
this.originalId,
this.retweet});
}
104 changes: 104 additions & 0 deletions frontend/lib/screens/main/posts/Item.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
import 'package:twitter/models/post.dart';
import 'package:twitter/models/user.dart';
import 'package:twitter/services/posts.dart';

class ItemPost extends StatefulWidget {
final PostModel post;
final AsyncSnapshot<UserModel> snapshotUser;
final AsyncSnapshot<bool> snapshotLike;
final AsyncSnapshot<bool> snapshotRetweet;
final bool retweet;

ItemPost(this.post, this.snapshotUser, this.snapshotLike,
this.snapshotRetweet, this.retweet,
{Key key})
: super(key: key);

@override
_ItemPostState createState() => _ItemPostState();
}

class _ItemPostState extends State<ItemPost> {
PostService _postService = PostService();
@override
Widget build(BuildContext context) {
return ListTile(
title: Padding(
padding: EdgeInsets.fromLTRB(0, 15, 0, 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.snapshotRetweet.data || widget.retweet)
Text("Retweet"),
SizedBox(height: 20),
Row(
children: [
widget.snapshotUser.data.profileImageUrl != ''
? CircleAvatar(
radius: 20,
backgroundImage: NetworkImage(
widget.snapshotUser.data.profileImageUrl))
: Icon(Icons.person, size: 40),
SizedBox(width: 10),
Text(widget.snapshotUser.data.name)
],
),
],
)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 15, 0, 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.post.text),
SizedBox(height: 20),
Text(widget.post.timestamp.toDate().toString()),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
IconButton(
icon: new Icon(
widget.snapshotRetweet.data
? Icons.cancel
: Icons.repeat,
color: Colors.blue,
size: 30.0),
onPressed: () => _postService.retweet(
widget.post, widget.snapshotRetweet.data)),
Text(widget.post.retweetsCount.toString())
],
),
Row(
children: [
IconButton(
icon: new Icon(
widget.snapshotLike.data
? Icons.favorite
: Icons.favorite_border,
color: Colors.blue,
size: 30.0),
onPressed: () {
_postService.likePost(
widget.post, widget.snapshotLike.data);
}),
Text(widget.post.likesCount.toString())
],
)
],
)
],
),
),
Divider(),
],
),
);
}
}
111 changes: 45 additions & 66 deletions frontend/lib/screens/main/posts/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:twitter/models/post.dart';
import 'package:twitter/models/user.dart';
import 'package:twitter/screens/main/posts/Item.dart';
import 'package:twitter/services/posts.dart';
import 'package:twitter/services/user.dart';

Expand All @@ -23,73 +24,51 @@ class _ListPostsState extends State<ListPosts> {
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
return StreamBuilder(
stream: _userService.getUserInfo(post.creator),
builder:
(BuildContext context, AsyncSnapshot<UserModel> snapshotUser) {
if (!snapshotUser.hasData) {
return Center(child: CircularProgressIndicator());
}

//stream builder to get user like
return StreamBuilder(
stream: _postService.getCurrentUserLike(post),
builder:
(BuildContext context, AsyncSnapshot<bool> snapshotLike) {
if (!snapshotLike.hasData) {
return Center(child: CircularProgressIndicator());
}

return ListTile(
title: Padding(
padding: EdgeInsets.fromLTRB(0, 15, 0, 15),
child: Row(
children: [
snapshotUser.data.profileImageUrl != ''
? CircleAvatar(
radius: 20,
backgroundImage: NetworkImage(
snapshotUser.data.profileImageUrl))
: Icon(Icons.person, size: 40),
SizedBox(width: 10),
Text(snapshotUser.data.name)
],
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 15, 0, 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(post.text),
SizedBox(height: 20),
Text(post.timestamp.toDate().toString()),
SizedBox(height: 20),
IconButton(
icon: new Icon(
snapshotLike.data
? Icons.favorite
: Icons.favorite_border,
color: Colors.blue,
size: 30.0),
onPressed: () {
_postService.likePost(
post, snapshotLike.data);
}),
Text(post.likesCount.toString())
],
),
),
Divider(),
],
),
);
});
});
if (post.retweet) {
return FutureBuilder(
future: _postService.getPostById(post.originalId),
builder: (BuildContext context,
AsyncSnapshot<PostModel> snapshotPost) {
if (!snapshotPost.hasData) {
return Center(child: CircularProgressIndicator());
}
return mainPost(snapshotPost.data, true);
});
}
return mainPost(post, false);
},
);
}

StreamBuilder<UserModel> mainPost(PostModel post, bool retweet) {
return StreamBuilder(
stream: _userService.getUserInfo(post.creator),
builder: (BuildContext context, AsyncSnapshot<UserModel> snapshotUser) {
if (!snapshotUser.hasData) {
return Center(child: CircularProgressIndicator());
}

//stream builder to get user like
return StreamBuilder(
stream: _postService.getCurrentUserLike(post),
builder:
(BuildContext context, AsyncSnapshot<bool> snapshotLike) {
if (!snapshotLike.hasData) {
return Center(child: CircularProgressIndicator());
} //stream builder to get user like

return StreamBuilder(
stream: _postService.getCurrentUserRetweet(post),
builder: (BuildContext context,
AsyncSnapshot<bool> snapshotRetweet) {
if (!snapshotLike.hasData) {
return Center(child: CircularProgressIndicator());
}

return ItemPost(post, snapshotUser, snapshotLike,
snapshotRetweet, retweet);
});
});
});
}
}
81 changes: 80 additions & 1 deletion frontend/lib/services/posts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,28 @@ class PostService {
creator: doc.data()['creator'] ?? '',
timestamp: doc.data()['timestamp'] ?? 0,
likesCount: doc.data()['likesCount'] ?? 0,
retweetsCount: doc.data()['retweetsCount'] ?? 0,
retweet: doc.data()['retweet'] ?? false,
originalId: doc.data()['originalId'] ?? null,
);
}).toList();
}

PostModel _postFromSnapshot(DocumentSnapshot snapshot) {
return snapshot.exists
? PostModel(
id: snapshot.id,
text: snapshot.data()['text'] ?? '',
creator: snapshot.data()['creator'] ?? '',
timestamp: snapshot.data()['timestamp'] ?? 0,
likesCount: snapshot.data()['likesCount'] ?? 0,
retweetsCount: snapshot.data()['retweetsCount'] ?? 0,
retweet: snapshot.data()['retweet'] ?? false,
originalId: snapshot.data()['originalId'] ?? null,
)
: null;
}

Future savePost(text) async {
await FirebaseFirestore.instance.collection("posts").add({
'text': text,
Expand All @@ -40,7 +58,6 @@ class PostService {
}
if (!current) {
post.likesCount = post.likesCount + 1;

await FirebaseFirestore.instance
.collection("posts")
.doc(post.id)
Expand All @@ -50,6 +67,49 @@ class PostService {
}
}

Future retweet(PostModel post, bool current) async {
if (current) {
post.retweetsCount = post.retweetsCount - 1;
await FirebaseFirestore.instance
.collection("posts")
.doc(post.id)
.collection("retweets")
.doc(FirebaseAuth.instance.currentUser.uid)
.delete();

await FirebaseFirestore.instance
.collection("posts")
.where("originalId", isEqualTo: post.id)
.where("creator", isEqualTo: FirebaseAuth.instance.currentUser.uid)
.get()
.then((value) {
if (value.docs.length == 0) {
return;
}
FirebaseFirestore.instance
.collection("posts")
.doc(value.docs[0].id)
.delete();
});
// Todo remove the retweet
return;
}
post.retweetsCount = post.retweetsCount + 1;
await FirebaseFirestore.instance
.collection("posts")
.doc(post.id)
.collection("retweets")
.doc(FirebaseAuth.instance.currentUser.uid)
.set({});

await FirebaseFirestore.instance.collection("posts").add({
'creator': FirebaseAuth.instance.currentUser.uid,
'timestamp': FieldValue.serverTimestamp(),
'retweet': true,
'originalId': post.id
});
}

Stream<bool> getCurrentUserLike(PostModel post) {
return FirebaseFirestore.instance
.collection("posts")
Expand All @@ -62,6 +122,25 @@ class PostService {
});
}

Stream<bool> getCurrentUserRetweet(PostModel post) {
return FirebaseFirestore.instance
.collection("posts")
.doc(post.id)
.collection("retweets")
.doc(FirebaseAuth.instance.currentUser.uid)
.snapshots()
.map((snapshot) {
return snapshot.exists;
});
}

Future<PostModel> getPostById(String id) async {
DocumentSnapshot postSnap =
await FirebaseFirestore.instance.collection("posts").doc(id).get();

return _postFromSnapshot(postSnap);
}

Stream<List<PostModel>> getPostsByUser(uid) {
return FirebaseFirestore.instance
.collection("posts")
Expand Down

0 comments on commit 4166c50

Please sign in to comment.