From 0b8c4ade43b9ac7eef622b16896932c1ae46b371 Mon Sep 17 00:00:00 2001 From: PaRaDoX50 <suryansh.stomar.min19@itbhu.ac.in> Date: Sun, 27 Jun 2021 04:12:22 +0530 Subject: [PATCH 1/2] Implement NewPostScreen and create Social Models and Entities --- app/src/main/AndroidManifest.xml | 5 + .../authentication/authentication_bloc.dart | 4 +- .../bookmarked_monuments_bloc.dart | 3 +- .../login_register/login_register_bloc.dart | 20 +- .../login_register/login_register_event.dart | 3 +- .../lib/blocs/new_post/new_post_bloc.dart | 49 +++ .../lib/blocs/new_post/new_post_event.dart | 14 + .../lib/blocs/new_post/new_post_state.dart | 23 ++ .../popular_monuments_bloc.dart | 4 +- .../lib/blocs/profile/profile_bloc.dart | 39 --- .../lib/blocs/profile/profile_event.dart | 13 - .../lib/blocs/profile/profile_state.dart | 23 -- monumento_module/lib/main.dart | 61 ++-- .../authentication_repository.dart | 14 +- .../authentication/entities/user_entity.dart | 55 ++-- .../firebase_authentication_repository.dart | 146 ++++++--- .../authentication/models/user_model.dart | 27 +- .../entities/bookmarked_monument_entity.dart | 30 +- .../monuments/entities/monument_entity.dart | 59 ++-- .../firebase_monument_repository.dart | 44 +-- .../monuments/monument_repository.dart | 12 +- .../social/entities/comment_entity.dart | 85 ++++++ .../social/entities/notification_entity.dart | 87 ++++++ .../social/entities/post_entity.dart | 81 +++++ .../social/firebase_social_repository.dart | 279 ++++++++++++++++++ .../social/models/comment_model.dart | 84 ++++++ .../social/models/notification_model.dart | 45 +++ .../resources/social/models/post_model.dart | 70 +++++ .../resources/social/social_repository.dart | 53 ++++ monumento_module/lib/screens/app_intro.dart | 1 - .../lib/screens/bookmark_screen.dart | 133 +++++---- .../lib/screens/detail_screen.dart | 111 +++---- .../lib/screens/feed/feed_screen.dart | 17 ++ monumento_module/lib/screens/home_screen.dart | 273 ++++++----------- .../lib/screens/new_post/new_post_screen.dart | 85 ++++++ monumento_module/lib/utils/image_picker.dart | 38 +++ monumento_module/pubspec.yaml | 33 ++- 37 files changed, 1541 insertions(+), 582 deletions(-) create mode 100644 monumento_module/lib/blocs/new_post/new_post_bloc.dart create mode 100644 monumento_module/lib/blocs/new_post/new_post_event.dart create mode 100644 monumento_module/lib/blocs/new_post/new_post_state.dart delete mode 100644 monumento_module/lib/blocs/profile/profile_bloc.dart delete mode 100644 monumento_module/lib/blocs/profile/profile_event.dart delete mode 100644 monumento_module/lib/blocs/profile/profile_state.dart create mode 100644 monumento_module/lib/resources/social/entities/comment_entity.dart create mode 100644 monumento_module/lib/resources/social/entities/notification_entity.dart create mode 100644 monumento_module/lib/resources/social/entities/post_entity.dart create mode 100644 monumento_module/lib/resources/social/firebase_social_repository.dart create mode 100644 monumento_module/lib/resources/social/models/comment_model.dart create mode 100644 monumento_module/lib/resources/social/models/notification_model.dart create mode 100644 monumento_module/lib/resources/social/models/post_model.dart create mode 100644 monumento_module/lib/resources/social/social_repository.dart create mode 100644 monumento_module/lib/screens/feed/feed_screen.dart create mode 100644 monumento_module/lib/screens/new_post/new_post_screen.dart create mode 100644 monumento_module/lib/utils/image_picker.dart diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b6b2fa9..3cc4cb8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,6 +26,11 @@ </intent-filter> </activity> <activity android:name=".SceneformFragment" /> + <activity + android:name="com.yalantis.ucrop.UCropActivity" + android:screenOrientation="portrait" + android:theme="@style/Theme.AppCompat.Light.NoActionBar"/> + <meta-data android:name="com.google.ar.core" diff --git a/monumento_module/lib/blocs/authentication/authentication_bloc.dart b/monumento_module/lib/blocs/authentication/authentication_bloc.dart index 85cdf01..37b1dfb 100644 --- a/monumento_module/lib/blocs/authentication/authentication_bloc.dart +++ b/monumento_module/lib/blocs/authentication/authentication_bloc.dart @@ -37,7 +37,7 @@ class AuthenticationBloc try { final user = await _authRepository.getUser(); if (user != null) { - yield Authenticated(UserModel.fromEntity(user)); + yield Authenticated(user); } else { yield Unauthenticated(); } @@ -49,7 +49,7 @@ class AuthenticationBloc Stream<AuthenticationState> _mapLoggedInToState() async* { final user = await _authRepository.getUser(); - yield Authenticated(UserModel.fromEntity(user)); + yield Authenticated(user); } Stream<AuthenticationState> _mapLoggedOutToState() async* { diff --git a/monumento_module/lib/blocs/bookmarked_monuments/bookmarked_monuments_bloc.dart b/monumento_module/lib/blocs/bookmarked_monuments/bookmarked_monuments_bloc.dart index 180800d..1990dc0 100644 --- a/monumento_module/lib/blocs/bookmarked_monuments/bookmarked_monuments_bloc.dart +++ b/monumento_module/lib/blocs/bookmarked_monuments/bookmarked_monuments_bloc.dart @@ -35,8 +35,7 @@ class BookmarkedMonumentsBloc Stream<BookmarkedMonumentsState> _mapRetrieveBookmarkedMonumentsToState( {String userId}) async* { _firebaseMonumentRepository.getBookmarkedMonuments(userId).listen((event) { - add(UpdateBookmarkedMonuments( - updatedBookmarkedMonuments: event.map((e) => BookmarkedMonumentModel.fromEntity(e)).toList())); + add(UpdateBookmarkedMonuments(updatedBookmarkedMonuments: event)); }); } diff --git a/monumento_module/lib/blocs/login_register/login_register_bloc.dart b/monumento_module/lib/blocs/login_register/login_register_bloc.dart index 9bc956c..5927887 100644 --- a/monumento_module/lib/blocs/login_register/login_register_bloc.dart +++ b/monumento_module/lib/blocs/login_register/login_register_bloc.dart @@ -38,7 +38,10 @@ class LoginRegisterBloc extends Bloc<LoginRegisterEvent, LoginRegisterState> { yield* _mapLogoutPressedToState(); } else if (event is SignUpWithEmailPressed) { yield* _mapSignUpWithEmailPressedToState( - email: event.email, password: event.password,name: event.name, status: event.status); + email: event.email, + password: event.password, + name: event.name, + status: event.status,username:event.username); } } @@ -47,7 +50,7 @@ class LoginRegisterBloc extends Bloc<LoginRegisterEvent, LoginRegisterState> { final user = await _authRepository.signInWithGoogle(); if (user != null) { _authenticationBloc.add(LoggedIn()); - yield LoginSuccess(UserModel.fromEntity(user)); + yield LoginSuccess(user); } else { yield LoginFailed(); } @@ -62,7 +65,7 @@ class LoginRegisterBloc extends Bloc<LoginRegisterEvent, LoginRegisterState> { final user = await _authRepository.emailSignIn(email: email, password: password); if (user != null) { - yield LoginSuccess(UserModel.fromEntity(user)); + yield LoginSuccess(user); } else { yield LoginFailed(); } @@ -78,11 +81,16 @@ class LoginRegisterBloc extends Bloc<LoginRegisterEvent, LoginRegisterState> { } Stream<LoginRegisterState> _mapSignUpWithEmailPressedToState( - {@required String email, @required String password, @required name, @required status}) async* { + {@required String email, + @required String password, + @required String name, + @required String status,@required String username}) async* { try { - final user = await _authRepository.signUp(email: email, name: name, password: password, status: status ); + //TODO implement profilePictureUpload feature + final user = await _authRepository.signUp( + email: email, name: name, password: password, status: status,username: username,profilePictureUrl: ""); if (user != null) { - yield SignUpSuccess(UserModel.fromEntity(user)); + yield SignUpSuccess(user); } else { yield SignUpFailed(); } diff --git a/monumento_module/lib/blocs/login_register/login_register_event.dart b/monumento_module/lib/blocs/login_register/login_register_event.dart index 9b3b826..f09d256 100644 --- a/monumento_module/lib/blocs/login_register/login_register_event.dart +++ b/monumento_module/lib/blocs/login_register/login_register_event.dart @@ -40,9 +40,10 @@ class SignUpWithEmailPressed extends LoginRegisterEvent { final String password; final String name; final String status; + final String username; - SignUpWithEmailPressed({@required this.email, @required this.password, @required this.name, @required this.status}); + SignUpWithEmailPressed({@required this.email, @required this.password, @required this.name, @required this.status,@required this.username}); @override List<Object> get props => [email, password]; diff --git a/monumento_module/lib/blocs/new_post/new_post_bloc.dart b/monumento_module/lib/blocs/new_post/new_post_bloc.dart new file mode 100644 index 0000000..bff350c --- /dev/null +++ b/monumento_module/lib/blocs/new_post/new_post_bloc.dart @@ -0,0 +1,49 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/social/models/post_model.dart'; +import 'package:monumento/resources/social/social_repository.dart'; + +part 'new_post_event.dart'; +part 'new_post_state.dart'; + +class NewPostBloc extends Bloc<NewPostEvent, NewPostState> { + final SocialRepository _socialRepository; + + NewPostBloc({SocialRepository socialRepository}) + : assert(socialRepository != null), + _socialRepository = socialRepository, + super(NewPostInitial()); + + @override + Stream<NewPostState> mapEventToState( + NewPostEvent event, + ) async* { + if (event is AddNewPost) { + yield* _mapAddNewPostToState( + image: event.image, location: event.location, title: event.title); + } + } + + Stream<NewPostState> _mapAddNewPostToState( + {@required File image, + @required String title, + @required String location}) async* { + try { + final String imageUrl = await _socialRepository.uploadImageForUrl( + file: image, address: "posts"); + if (imageUrl != null) { + final PostModel post = await _socialRepository.uploadNewPost( + title: title, location: location, imageUrl: imageUrl); + yield NewPostAdded(post: post); + } else { + yield NewPostFailed(); + } + } catch (_) { + yield NewPostFailed(); + } + } +} diff --git a/monumento_module/lib/blocs/new_post/new_post_event.dart b/monumento_module/lib/blocs/new_post/new_post_event.dart new file mode 100644 index 0000000..4e979eb --- /dev/null +++ b/monumento_module/lib/blocs/new_post/new_post_event.dart @@ -0,0 +1,14 @@ +part of 'new_post_bloc.dart'; + +@immutable +abstract class NewPostEvent extends Equatable {} + +class AddNewPost extends NewPostEvent { + final String title; + final String location; + final File image; + AddNewPost( + {@required this.image, @required this.location, @required this.title}); + @override + List<Object> get props => []; +} diff --git a/monumento_module/lib/blocs/new_post/new_post_state.dart b/monumento_module/lib/blocs/new_post/new_post_state.dart new file mode 100644 index 0000000..78cb4ca --- /dev/null +++ b/monumento_module/lib/blocs/new_post/new_post_state.dart @@ -0,0 +1,23 @@ +part of 'new_post_bloc.dart'; + +@immutable +abstract class NewPostState extends Equatable {} + +class NewPostInitial extends NewPostState { + @override + // TODO: implement props + List<Object> get props => throw UnimplementedError(); +} + +class NewPostAdded extends NewPostState { + final PostModel post; + NewPostAdded({this.post}); + @override + List<Object> get props => [post]; +} + +class NewPostFailed extends NewPostState { + @override + // TODO: implement props + List<Object> get props => throw UnimplementedError(); +} diff --git a/monumento_module/lib/blocs/popular_monuments/popular_monuments_bloc.dart b/monumento_module/lib/blocs/popular_monuments/popular_monuments_bloc.dart index 9eea588..a0d2db2 100644 --- a/monumento_module/lib/blocs/popular_monuments/popular_monuments_bloc.dart +++ b/monumento_module/lib/blocs/popular_monuments/popular_monuments_bloc.dart @@ -33,10 +33,10 @@ class PopularMonumentsBloc Stream<PopularMonumentsState> _mapGetPopularMonumentsToState() async* { try { - final List<MonumentEntity> popularMonuments = + final List<MonumentModel> popularMonuments = await _firebaseMonumentRepository.getPopularMonuments(); - yield PopularMonumentsRetrieved(popularMonuments: popularMonuments.map((e) => MonumentModel.fromEntity(e)).toList()); + yield PopularMonumentsRetrieved(popularMonuments: popularMonuments); } catch (_) { yield FailedToRetrievePopularMonuments(); } diff --git a/monumento_module/lib/blocs/profile/profile_bloc.dart b/monumento_module/lib/blocs/profile/profile_bloc.dart deleted file mode 100644 index 61476e2..0000000 --- a/monumento_module/lib/blocs/profile/profile_bloc.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:async'; - -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; -import 'package:meta/meta.dart'; -import 'package:monumento/resources/authentication/entities/user_entity.dart'; -import 'package:monumento/resources/authentication/models/user_model.dart'; -import 'package:monumento/resources/monuments/monument_repository.dart'; - -part 'profile_event.dart'; -part 'profile_state.dart'; - -class ProfileBloc extends Bloc<ProfileEvent, ProfileState> { - MonumentRepository _firebaseMonumentRepository; - ProfileBloc({@required MonumentRepository firebaseMonumentRepository}) - : assert(firebaseMonumentRepository != null), - _firebaseMonumentRepository = firebaseMonumentRepository, - super(ProfileInitial()); - - @override - Stream<ProfileState> mapEventToState( - ProfileEvent event, - ) async* { - if (event is GetProfileData) { - yield* _mapGetProfileDataToState(userId: event.userId); - } - } - - Stream<ProfileState> _mapGetProfileDataToState({String userId}) async* { - try { - final UserEntity profileData = - await _firebaseMonumentRepository.getProfileData(userId); - - yield ProfileDataRetrieved(profile: UserModel.fromEntity(profileData)); - } catch (_) { - yield FailedToRetrieveProfileData(); - } - } -} diff --git a/monumento_module/lib/blocs/profile/profile_event.dart b/monumento_module/lib/blocs/profile/profile_event.dart deleted file mode 100644 index a89a500..0000000 --- a/monumento_module/lib/blocs/profile/profile_event.dart +++ /dev/null @@ -1,13 +0,0 @@ -part of 'profile_bloc.dart'; - -abstract class ProfileEvent extends Equatable { - const ProfileEvent(); -} - -class GetProfileData extends ProfileEvent { - final String userId; - GetProfileData({this.userId}); - @override - // TODO: implement props - List<Object> get props => [userId]; -} diff --git a/monumento_module/lib/blocs/profile/profile_state.dart b/monumento_module/lib/blocs/profile/profile_state.dart deleted file mode 100644 index 340ae21..0000000 --- a/monumento_module/lib/blocs/profile/profile_state.dart +++ /dev/null @@ -1,23 +0,0 @@ -part of 'profile_bloc.dart'; - -abstract class ProfileState extends Equatable { - const ProfileState(); -} - -class ProfileInitial extends ProfileState { - @override - List<Object> get props => []; -} - -class ProfileDataRetrieved extends ProfileState { - final UserModel profile; - - ProfileDataRetrieved({this.profile}); - @override - List<Object> get props => []; -} - -class FailedToRetrieveProfileData extends ProfileState { - @override - List<Object> get props => []; -} diff --git a/monumento_module/lib/main.dart b/monumento_module/lib/main.dart index 5ce3bbb..23fc0fa 100644 --- a/monumento_module/lib/main.dart +++ b/monumento_module/lib/main.dart @@ -2,6 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:monumento/blocs/new_post/new_post_bloc.dart'; +import 'package:monumento/resources/social/firebase_social_repository.dart'; +import 'package:monumento/resources/social/social_repository.dart'; import 'package:monumento/screens/app_intro.dart'; import 'package:monumento/blocs/authentication/authentication_bloc.dart'; import 'package:monumento/blocs/bookmarked_monuments/bookmarked_monuments_bloc.dart'; @@ -10,10 +13,15 @@ import 'package:monumento/blocs/popular_monuments/popular_monuments_bloc.dart'; import 'package:monumento/blocs/profile/profile_bloc.dart'; import 'package:monumento/resources/authentication/firebase_authentication_repository.dart'; import 'package:monumento/resources/monuments/firebase_monument_repository.dart'; +import 'package:monumento/screens/feed/feed_screen.dart'; +import 'package:monumento/screens/new_post/new_post_screen.dart'; import 'screens/home_screen.dart'; +import 'package:firebase_core/firebase_core.dart'; Future<Null> main() async { WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + runApp(MyApp()); } @@ -25,14 +33,16 @@ class MyApp extends StatefulWidget { class _MyAppState extends State<MyApp> { final FirebaseAuthenticationRepository _authRepository = - FirebaseAuthenticationRepository(); + FirebaseAuthenticationRepository(); final FirebaseMonumentRepository _monumentRepository = - FirebaseMonumentRepository(); + FirebaseMonumentRepository(); + final FirebaseSocialRepository _socialRepository = FirebaseSocialRepository(); AuthenticationBloc _authenticationBloc; LoginRegisterBloc _loginRegisterBloc; ProfileBloc _profileBloc; BookmarkedMonumentsBloc _bookmarkedMonumentsBloc; PopularMonumentsBloc _popularMonumentsBloc; + NewPostBloc _newPostBloc; @override void initState() { @@ -47,7 +57,7 @@ class _MyAppState extends State<MyApp> { firebaseMonumentRepository: _monumentRepository); _popularMonumentsBloc = PopularMonumentsBloc(firebaseMonumentRepository: _monumentRepository); - + _newPostBloc = NewPostBloc(socialRepository: _socialRepository); _popularMonumentsBloc.add(GetPopularMonuments()); _authenticationBloc.add(AppStarted()); } @@ -72,27 +82,36 @@ class _MyAppState extends State<MyApp> { ), BlocProvider<BookmarkedMonumentsBloc>( create: (_) => _bookmarkedMonumentsBloc, - ) + ), + + BlocProvider<NewPostBloc>( + create: (_) => _newPostBloc, + ), + + ], child: MaterialApp( - title: 'Monumento', - debugShowCheckedModeBanner: false, - theme: ThemeData( - primarySwatch: Colors.amber, - fontFamily: GoogleFonts.montserrat().fontFamily), - home: BlocBuilder<AuthenticationBloc, AuthenticationState>( - builder: (BuildContext context, AuthenticationState state) { - if (state is Authenticated) { - return HomeScreen( - user: state.user, + title: 'Monumento', + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.amber, + fontFamily: GoogleFonts.montserrat().fontFamily), + home: BlocBuilder<AuthenticationBloc, AuthenticationState>( + builder: (BuildContext context, AuthenticationState state) { + if (state is Authenticated) { + return HomeScreen( + user: state.user, + ); + // return Scaffold(body: FeedScreen()); + } else if (state is Unauthenticated) { + return AppIntroPage(); + } + return Scaffold( + backgroundColor: Colors.white, ); - } else if (state is Unauthenticated) { - return AppIntroPage(); - } - return Scaffold( - backgroundColor: Colors.white, - ); - })), + }), + routes: {'/newPostScreen': (_) => NewPostScreen()}, + ), ); } diff --git a/monumento_module/lib/resources/authentication/authentication_repository.dart b/monumento_module/lib/resources/authentication/authentication_repository.dart index 28f3714..030a80c 100644 --- a/monumento_module/lib/resources/authentication/authentication_repository.dart +++ b/monumento_module/lib/resources/authentication/authentication_repository.dart @@ -1,17 +1,21 @@ import 'package:meta/meta.dart'; -import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; abstract class AuthenticationRepository { - Future<UserEntity> emailSignIn( + Future<UserModel> emailSignIn( {@required String email, @required String password}); - Future<UserEntity> signInWithGoogle(); + Future<UserModel> signInWithGoogle(); - Future<UserEntity> signUp({@required String email, @required String password, @required String name,@required String status }); + Future<UserModel> signUp( + {@required String email, + @required String password, + @required String name, + @required String status,@required String username,@required String profilePictureUrl}); Future<void> signOut(); Future<bool> isSignedIn(); - Future<UserEntity> getUser(); + Future<UserModel> getUser(); } diff --git a/monumento_module/lib/resources/authentication/entities/user_entity.dart b/monumento_module/lib/resources/authentication/entities/user_entity.dart index 92866cd..1b17b8f 100644 --- a/monumento_module/lib/resources/authentication/entities/user_entity.dart +++ b/monumento_module/lib/resources/authentication/entities/user_entity.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:equatable/equatable.dart'; @@ -7,50 +9,53 @@ class UserEntity extends Equatable { final String name; final String profilePictureUrl; final String status; + final String username; + UserEntity( - {this.email, this.uid, this.name = "Monumento User", this.profilePictureUrl, this.status=" hgh"}); + {this.email, + this.uid, + this.name = "Monumento User", + this.profilePictureUrl, + this.status = " hgh",this.username}); @override List<Object> get props { return [uid]; } - Map<String, Object> toJson() { - return { - 'uid': uid, - 'name': name, - 'email': email, - 'profilePictureUrl': profilePictureUrl, - 'status': status - }; - } - - static UserEntity fromJson(Map<String, Object> json) { + factory UserEntity.fromMap(Map<String, Object> data) { return UserEntity( - uid: json['uid'] as String, - name: json['name'] as String, - email: json['email'] as String, - profilePictureUrl: json['profilePictureUrl'] as String, - status: json['status'] as String); + uid: data['uid'] as String, + name: data['name'] as String, + email: data['email'] as String, + profilePictureUrl: data['profilePictureUrl'] as String, + status: data['status'] as String, username: data['username'] as String); } - static UserEntity fromSnapshot(DocumentSnapshot snap) { + factory UserEntity.fromSnapshot(DocumentSnapshot snap) { + Map<String, dynamic> data = snap.data(); + return UserEntity( - uid: snap.data['uid'], - name: snap.data['name'], - email: snap.data['email'], - profilePictureUrl: snap.data['profilePictureUrl'], - status: snap.data['status']); + uid: data['uid'], + name: data['name'], + email: data['email'], + profilePictureUrl: data['profilePictureUrl'], + status: data['status'], username: data['username']); } + factory UserEntity.fromJson(String source) => + UserEntity.fromMap(json.decode(source)); - Map<String, Object> toDocument() { + Map<String, Object> toMap() { return { 'uid': uid, 'name': name, 'email': email, 'profilePictureUrl': profilePictureUrl, - 'status': status + 'status': status, + 'username':username }; } + + String toJson() => json.encode(toMap()); } diff --git a/monumento_module/lib/resources/authentication/firebase_authentication_repository.dart b/monumento_module/lib/resources/authentication/firebase_authentication_repository.dart index 061bd0c..2e05038 100644 --- a/monumento_module/lib/resources/authentication/firebase_authentication_repository.dart +++ b/monumento_module/lib/resources/authentication/firebase_authentication_repository.dart @@ -4,56 +4,71 @@ import 'package:flutter/foundation.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:monumento/resources/authentication/authentication_repository.dart'; import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; class FirebaseAuthenticationRepository implements AuthenticationRepository { final FirebaseAuth _firebaseAuth; final GoogleSignIn _googleSignIn; - final Firestore _database; + final FirebaseFirestore _database; - FirebaseAuthenticationRepository( - {FirebaseAuth firebaseAuth, - GoogleSignIn googleSignin, - Firestore databaseInstance}) + FirebaseAuthenticationRepository({FirebaseAuth firebaseAuth, + GoogleSignIn googleSignin, + FirebaseFirestore databaseInstance}) : _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance, _googleSignIn = googleSignin ?? GoogleSignIn(), - _database = databaseInstance ?? Firestore.instance; + _database = databaseInstance ?? FirebaseFirestore.instance; - Future<UserEntity> emailSignIn( + Future<UserModel> emailSignIn( {@required String email, @required String password}) async { - AuthResult authResult = await _firebaseAuth.signInWithEmailAndPassword( - email: email, password: password); - FirebaseUser user = authResult.user; - DocumentSnapshot userDocSnap = await _database.collection("users").document(user.uid).get(); - return UserEntity.fromSnapshot(userDocSnap); - // return user; + UserCredential userCredential = await _firebaseAuth + .signInWithEmailAndPassword(email: email, password: password); + + User user = userCredential.user; + DocumentSnapshot userDocSnap = + await _database.collection("users").doc(user.uid).get(); + return UserModel.fromEntity(userEntity:UserEntity.fromSnapshot(userDocSnap),snapshot: userDocSnap); } - Future<UserEntity> signInWithGoogle() async { + Future<UserModel> signInWithGoogle() async { print('Google Sign In called'); final GoogleSignInAccount googleSignInAccount = - await _googleSignIn.signIn(); + await _googleSignIn.signIn(); final GoogleSignInAuthentication googleSignInAuthentication = - await googleSignInAccount.authentication; - - final AuthCredential credential = GoogleAuthProvider.getCredential( + await googleSignInAccount.authentication; + final AuthCredential credential = GoogleAuthProvider.credential( accessToken: googleSignInAuthentication.accessToken, idToken: googleSignInAuthentication.idToken, ); - FirebaseUser currentUser = await _firebaseAuth.currentUser(); - - DocumentSnapshot userDocSnap = await getOrCreateUserDoc(currentUser,false); + User currentUser = _firebaseAuth.currentUser; + //TODO : Complete getOrCreateUserDocForGoogleSignIn. Once the user signIns using the Google Provider, + // take him to the new form where he enters his username, chooses profile picture and reviews other info + DocumentSnapshot userDocSnap = await getOrCreateUserDocForGoogleSignIn( + profilePictureUrl: "", + username: currentUser.email.split("@")[0], + currentUser: currentUser, + name: currentUser.displayName, + status: ""); - return UserEntity.fromSnapshot(userDocSnap); + return UserModel.fromEntity(userEntity:UserEntity.fromSnapshot(userDocSnap),snapshot: userDocSnap); } - Future<UserEntity> signUp({@required String email, @required String password, @required String name,@required String status }) async { - final AuthResult authResult = await _firebaseAuth + Future<UserModel> signUp({@required String email, + @required String password, + @required String name, + @required String status, @required String username, @required String profilePictureUrl}) async { + final UserCredential userCredential = await _firebaseAuth .createUserWithEmailAndPassword(email: email, password: password); - final FirebaseUser currentUser = await _firebaseAuth.currentUser(); + final User currentUser = _firebaseAuth.currentUser; - DocumentSnapshot userDocSnap = await getOrCreateUserDoc(currentUser,true,status: status,name: name); + DocumentSnapshot userDocSnap = + await getOrCreateUserDocForEmailSignup(status: status, + name: name, + username: username, + email: email, + profilePictureUrl: profilePictureUrl, + uid: currentUser.uid); - return UserEntity.fromSnapshot(userDocSnap); + return UserModel.fromEntity(userEntity:UserEntity.fromSnapshot(userDocSnap),snapshot: userDocSnap); } Future<void> signOut() async { @@ -64,30 +79,75 @@ class FirebaseAuthenticationRepository implements AuthenticationRepository { } Future<bool> isSignedIn() async { - final currentUser = await _firebaseAuth.currentUser(); + final currentUser = _firebaseAuth.currentUser; + return currentUser != null; } - Future<UserEntity> getUser() async { - FirebaseUser currentUser = (await _firebaseAuth.currentUser()); - DocumentSnapshot userDocSnap = await _database.collection("users").document(currentUser.uid).get(); - return UserEntity.fromSnapshot(userDocSnap); + Future<UserModel> getUser() async { + User currentUser = (_firebaseAuth.currentUser); + DocumentSnapshot userDocSnap = + await _database.collection("users").doc(currentUser.uid).get(); + return UserModel.fromEntity(userEntity:UserEntity.fromSnapshot(userDocSnap),snapshot: userDocSnap); + } + + Future<DocumentSnapshot> getOrCreateUserDocForEmailSignup( + {String uid, String name, String status, String username, String email, String profilePictureUrl}) async { + DocumentSnapshot userDocSnap = + await _database.collection("users").doc(uid).get(); + if (userDocSnap.exists) { + return userDocSnap; + } + List<String> searchParams = getSearchParams(name: name,userName: username); + + await _database.collection("users").doc(uid).set({ + 'name': name, + 'uid': uid, + 'profilePictureUrl': profilePictureUrl ?? "", + 'email': email, + 'status': status ?? "", + 'username': username, + 'searchParams': searchParams + }); + DocumentSnapshot newUserDocSnap = + await _database.collection("users").doc(uid).get(); + return newUserDocSnap; } - Future<DocumentSnapshot> getOrCreateUserDoc(FirebaseUser currentUser, bool isEmailSignUp, - {String name, String status}) async { - DocumentSnapshot userDocSnap = await _database.collection("users").document(currentUser.uid).get(); - if(userDocSnap.exists){ + Future<DocumentSnapshot> getOrCreateUserDocForGoogleSignIn( + {User currentUser, String name, String status, String username, String profilePictureUrl}) async { + DocumentSnapshot userDocSnap = + await _database.collection("users").doc(currentUser.uid).get(); + if (userDocSnap.exists) { return userDocSnap; } - await _database.collection("users").document(currentUser.uid).setData({ - 'name': isEmailSignUp ? name : currentUser.displayName, - 'uid':currentUser.uid, - 'profilePictureUrl':'', - 'email':currentUser.email, - 'status': isEmailSignUp ? status : "" , + List<String> searchParams = getSearchParams(name: name,userName: username); + + + await _database.collection("users").doc(currentUser.uid).set({ + 'name': name, + 'uid': currentUser.uid, + 'profilePictureUrl': profilePictureUrl ?? "", + 'email': currentUser.email, + 'status': status ?? "", + 'username': username, + 'searchParams': searchParams }); - DocumentSnapshot newUserDocSnap = await _database.collection("users").document(currentUser.uid).get(); + DocumentSnapshot newUserDocSnap = + await _database.collection("users").doc(currentUser.uid).get(); return newUserDocSnap; } + + List<String> getSearchParams({String userName, String name}) { + List<String> searchParams = []; + for (int i = 0; i < userName.length; i++) { + print(userName.substring(0, i + 1)); + searchParams.add(userName.toLowerCase().substring(0, i + 1).replaceAll(' ', '')); + } + for (int i = 0; i < name.trim().length; i++) { + print(name.trim().substring(0, i + 1)); + searchParams.add(name.trim().toLowerCase().substring(0, i + 1).replaceAll(' ', '')); + } + return searchParams; + } } diff --git a/monumento_module/lib/resources/authentication/models/user_model.dart b/monumento_module/lib/resources/authentication/models/user_model.dart index 44a5e8a..bdb78a2 100644 --- a/monumento_module/lib/resources/authentication/models/user_model.dart +++ b/monumento_module/lib/resources/authentication/models/user_model.dart @@ -1,3 +1,5 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:meta/meta.dart'; import 'package:monumento/resources/authentication/entities/user_entity.dart'; class UserModel { @@ -6,17 +8,19 @@ class UserModel { final String name; final String profilePictureUrl; final String status; + final String username; + final DocumentSnapshot documentSnapshot; UserModel( - {this.email, this.uid, this.name, this.profilePictureUrl, this.status = ""}); + {this.email, this.uid, this.name, this.profilePictureUrl, this.status = "",this.username,this.documentSnapshot}); - UserModel copyWith() { + UserModel copyWith({String email,String name,String profilePictureUrl,String status,String username,String uid}) { return UserModel( - email: email, - uid: uid, - name: name, - profilePictureUrl: profilePictureUrl, - status: status); + email: email ?? this.email, + uid: uid ?? this.uid, + name: name ?? this.name, + profilePictureUrl: profilePictureUrl ?? this.profilePictureUrl, + status: status ?? this.status,username: username ?? this.username,documentSnapshot: documentSnapshot); } UserEntity toEntity() { @@ -28,12 +32,17 @@ class UserModel { status: status); } - static UserModel fromEntity(UserEntity userEntity) { + static UserModel fromEntity( + {@required UserEntity userEntity, DocumentSnapshot snapshot}) { return UserModel( uid: userEntity.uid, name: userEntity.name, email: userEntity.email, profilePictureUrl: userEntity.profilePictureUrl, - status: userEntity.status); + status: userEntity.status,username: userEntity.username,documentSnapshot: snapshot); + } + @override + String toString() { + return 'UserModel(profilePictureUrl:$profilePictureUrl)'; } } diff --git a/monumento_module/lib/resources/monuments/entities/bookmarked_monument_entity.dart b/monumento_module/lib/resources/monuments/entities/bookmarked_monument_entity.dart index 7fc81c0..3bb1bb7 100644 --- a/monumento_module/lib/resources/monuments/entities/bookmarked_monument_entity.dart +++ b/monumento_module/lib/resources/monuments/entities/bookmarked_monument_entity.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:equatable/equatable.dart'; import 'package:monumento/resources/monuments/entities/monument_entity.dart'; @@ -11,7 +13,7 @@ class BookmarkedMonumentEntity extends Equatable { @override List<Object> get props => [bookmarkedByUid, monumentEntity.id]; - Map<String, Object> toJson() { + Map<String, Object> toMap() { return { 'bookmarkedByUid': bookmarkedByUid, 'id': monumentEntity.id, @@ -22,26 +24,22 @@ class BookmarkedMonumentEntity extends Equatable { }; } - static BookmarkedMonumentEntity fromJson(Map<String, Object> json) { + factory BookmarkedMonumentEntity.fromMap(Map<String, Object> data) { return BookmarkedMonumentEntity( - monumentEntity: MonumentEntity.fromJson(json), - bookmarkedByUid: json['bookmarkedByUid']); + monumentEntity: MonumentEntity.fromMap(data), + bookmarkedByUid: data['bookmarkedByUid']); } - static BookmarkedMonumentEntity fromSnapshot(DocumentSnapshot snap) { + factory BookmarkedMonumentEntity.fromSnapshot(DocumentSnapshot snap) { + final Map<String, dynamic> data = snap.data(); + return BookmarkedMonumentEntity( - bookmarkedByUid: snap.data['bookmarkedByUid'], + bookmarkedByUid: data['bookmarkedByUid'], monumentEntity: MonumentEntity.fromSnapshot(snap)); } - Map<String, Object> toDocument() { - return { - 'bookmarkedByUid':bookmarkedByUid, - 'id': monumentEntity.id, - 'name': monumentEntity.name, - 'city': monumentEntity.city, - 'country': monumentEntity.country, - 'image': monumentEntity.imageUrl, - }; - } + BookmarkedMonumentEntity fromJson(String source) => + BookmarkedMonumentEntity.fromMap(json.decode(source)); + + String toJson() => json.encode(toMap()); } diff --git a/monumento_module/lib/resources/monuments/entities/monument_entity.dart b/monumento_module/lib/resources/monuments/entities/monument_entity.dart index 285e6bf..14802b7 100644 --- a/monumento_module/lib/resources/monuments/entities/monument_entity.dart +++ b/monumento_module/lib/resources/monuments/entities/monument_entity.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; @@ -9,45 +11,39 @@ class MonumentEntity extends Equatable { final String country; final String imageUrl; final String wiki; - MonumentEntity({this.id, this.city, this.country, this.imageUrl, this.name, @required this.wiki}); + MonumentEntity( + {this.id, + this.city, + this.country, + this.imageUrl, + this.name, + @required this.wiki}); @override List<Object> get props => [id]; - Map<String, Object> toJson() { - return { - 'id': id, - 'name': name, - 'city': city, - 'country': country, - 'image': imageUrl, - 'wiki':wiki - }; - } - - static MonumentEntity fromJson(Map<String, Object> json) { + factory MonumentEntity.fromMap(Map<String, Object> data) { return MonumentEntity( - id: json['id'] as String, - name: json['name'] as String, - city: json['email'] as String, - country: json['country'] as String, - imageUrl: json['image'] as String, - wiki: json['wiki'] as String - ); + id: data['id'] as String, + name: data['name'] as String, + city: data['email'] as String, + country: data['country'] as String, + imageUrl: data['image'] as String, + wiki: data['wiki'] as String); } - static MonumentEntity fromSnapshot(DocumentSnapshot snap) { + factory MonumentEntity.fromSnapshot(DocumentSnapshot snap) { + final Map<String, dynamic> data = snap.data(); return MonumentEntity( - id: snap.data['id'], - name: snap.data['name'], - city: snap.data['city'], - country: snap.data['country'], - imageUrl: snap.data['image'], - wiki: snap.data['wiki'] - ); + id: data['id'], + name: data['name'], + city: data['city'], + country: data['country'], + imageUrl: data['image'], + wiki: data['wiki']); } - Map<String, Object> toDocument() { + Map<String, Object> toMap() { return { 'id': id, 'name': name, @@ -57,4 +53,9 @@ class MonumentEntity extends Equatable { 'wiki': wiki }; } + + String toJson() => json.encode(toMap()); + + factory MonumentEntity.fromJson(String source) => + MonumentEntity.fromMap(json.decode(source)); } diff --git a/monumento_module/lib/resources/monuments/firebase_monument_repository.dart b/monumento_module/lib/resources/monuments/firebase_monument_repository.dart index a852660..9c1197d 100644 --- a/monumento_module/lib/resources/monuments/firebase_monument_repository.dart +++ b/monumento_module/lib/resources/monuments/firebase_monument_repository.dart @@ -1,36 +1,46 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; import 'package:monumento/resources/monuments/entities/bookmarked_monument_entity.dart'; import 'package:monumento/resources/monuments/entities/monument_entity.dart'; import 'package:monumento/resources/monuments/monument_repository.dart'; +import 'models/bookmarked_monument_model.dart'; +import 'models/monument_model.dart'; + class FirebaseMonumentRepository implements MonumentRepository { - Firestore _database = Firestore.instance; - Future<List<MonumentEntity>> getPopularMonuments() async { - final docs = await _database.collection('popular_monuments').getDocuments(); - final List<MonumentEntity> popularMonumentsDocs = docs.documents.map((doc) => MonumentEntity.fromSnapshot(doc)).toList(); - return popularMonumentsDocs; + FirebaseFirestore _database; + FirebaseMonumentRepository({FirebaseFirestore database}) + : _database = database ?? FirebaseFirestore.instance; + + Future<List<MonumentModel>> getPopularMonuments() async { + final docs = await _database.collection('popular_monuments').get(); + final List<MonumentModel> popularMonumentsDocs = docs.docs + .map( + (doc) => MonumentModel.fromEntity(MonumentEntity.fromSnapshot(doc))) + .toList(); + return popularMonumentsDocs; } - Stream<List<BookmarkedMonumentEntity>> getBookmarkedMonuments(String userId) { - Stream<List<BookmarkedMonumentEntity>> streamBookmarkedMonuments = Firestore.instance + Stream<List<BookmarkedMonumentModel>> getBookmarkedMonuments(String userId) { + Stream<List<BookmarkedMonumentModel>> streamBookmarkedMonuments = _database .collection('bookmarks') .where("uid", isEqualTo: userId) - .snapshots().map((querySnap) => querySnap.documents.map((doc) => BookmarkedMonumentEntity.fromSnapshot(doc)).toList()); - - + .snapshots() + .map((querySnap) => querySnap.docs + .map((doc) => BookmarkedMonumentModel.fromEntity( + BookmarkedMonumentEntity.fromSnapshot(doc))) + .toList()); return streamBookmarkedMonuments; } - Future<UserEntity> getProfileData(String userId) async { - final snap = await Firestore.instance - .collection('users') - .document(userId) - .get(); - if(snap.exists){ - return UserEntity.fromSnapshot(snap); + Future<UserModel> getProfileData(String userId) async { + final snap = await _database.collection('users').doc(userId).get(); + if (snap.exists) { + return UserModel.fromEntity( + userEntity: UserEntity.fromSnapshot(snap), snapshot: snap); } return null; } diff --git a/monumento_module/lib/resources/monuments/monument_repository.dart b/monumento_module/lib/resources/monuments/monument_repository.dart index b63fd85..62ba425 100644 --- a/monumento_module/lib/resources/monuments/monument_repository.dart +++ b/monumento_module/lib/resources/monuments/monument_repository.dart @@ -1,9 +1,15 @@ import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; import 'package:monumento/resources/monuments/entities/bookmarked_monument_entity.dart'; import 'package:monumento/resources/monuments/entities/monument_entity.dart'; +import 'models/bookmarked_monument_model.dart'; +import 'models/monument_model.dart'; + abstract class MonumentRepository { - Future<List<MonumentEntity>> getPopularMonuments(); - Stream<List<BookmarkedMonumentEntity>> getBookmarkedMonuments(String userId); - Future<UserEntity> getProfileData(String userId); + //TODO convert entity->model + //TODO shift profieldata method to social repo + Future<List<MonumentModel>> getPopularMonuments(); + Stream<List<BookmarkedMonumentModel>> getBookmarkedMonuments(String userId); + Future<UserModel> getProfileData(String userId); } diff --git a/monumento_module/lib/resources/social/entities/comment_entity.dart b/monumento_module/lib/resources/social/entities/comment_entity.dart new file mode 100644 index 0000000..5fce718 --- /dev/null +++ b/monumento_module/lib/resources/social/entities/comment_entity.dart @@ -0,0 +1,85 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/social/entities/post_entity.dart'; + +class CommentEntity{ + final String comment; + final String postInvolvedId; + final UserEntity author; + final int timeStamp; + + + const CommentEntity({ + @required this.comment, + @required this.postInvolvedId, + @required this.author, + @required this.timeStamp, + }); + + CommentEntity copyWith({ + String comment, + String postInvolvedId, + UserEntity author, + int timeStamp, + }) { + return new CommentEntity( + comment: comment ?? this.comment, + postInvolvedId: postInvolvedId ?? this.postInvolvedId, + author: author ?? this.author, + timeStamp: timeStamp ?? this.timeStamp, + ); + } + + @override + String toString() { + return 'CommentEntity{comment: $comment, postInvolvedId: $postInvolvedId, author: $author, timeStamp: $timeStamp}'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + (other is CommentEntity && + runtimeType == other.runtimeType && + comment == other.comment && + postInvolvedId == other.postInvolvedId && + author == other.author && + timeStamp == other.timeStamp); + + @override + int get hashCode => + comment.hashCode ^ + postInvolvedId.hashCode ^ + author.hashCode ^ + timeStamp.hashCode; + + factory CommentEntity.fromMap(Map<String, dynamic> map) { + return new CommentEntity( + comment: map['comment'] as String, + postInvolvedId: map['postInvolvedId'] as String, + author: map['author'] as UserEntity, + timeStamp: map['timeStamp'] as int, + ); + } + + factory CommentEntity.fromSnapshot(DocumentSnapshot snapshot) { + Map<String,dynamic> data = snapshot.data(); + return new CommentEntity( + comment: data['comment'] as String, + postInvolvedId: data['postInvolvedId'] as String, + author: UserEntity.fromMap(data['author']), + timeStamp: data['timeStamp'] as int, + ); + } + + Map<String, dynamic> toMap() { + return { + 'comment': this.comment, + 'postInvolvedId': this.postInvolvedId, + 'author': this.author.toMap(), + 'timeStamp': this.timeStamp, + }; + } + + +} \ No newline at end of file diff --git a/monumento_module/lib/resources/social/entities/notification_entity.dart b/monumento_module/lib/resources/social/entities/notification_entity.dart new file mode 100644 index 0000000..4f55b05 --- /dev/null +++ b/monumento_module/lib/resources/social/entities/notification_entity.dart @@ -0,0 +1,87 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/social/entities/post_entity.dart'; + +class NotificationEntity{ + final int notificationType; + final UserEntity userInvolved; + final PostEntity postInvolved; + final int timeStamp; + + + const NotificationEntity({ + @required this.notificationType, + this.userInvolved, + this.postInvolved, + @required this.timeStamp, + }); + + NotificationEntity copyWith({ + int notificationType, + UserEntity userInvolved, + PostEntity postInvolved, + int timeStamp, + }) { + return NotificationEntity( + notificationType: notificationType ?? this.notificationType, + userInvolved: userInvolved ?? this.userInvolved, + postInvolved: postInvolved ?? this.postInvolved, + timeStamp: timeStamp ?? this.timeStamp, + ); + + } + + @override + String toString() { + return 'NotificationEntity{notificationType: $notificationType, userInvolved: $userInvolved, postInvolved: $postInvolved, timeStamp: $timeStamp}'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + (other is NotificationEntity && + runtimeType == other.runtimeType && + notificationType == other.notificationType && + userInvolved == other.userInvolved && + postInvolved == other.postInvolved && + timeStamp == other.timeStamp); + + @override + int get hashCode => + notificationType.hashCode ^ + userInvolved.hashCode ^ + postInvolved.hashCode ^ + timeStamp.hashCode; + + factory NotificationEntity.fromMap(Map<String, dynamic> map) { + return NotificationEntity( + notificationType: map['notificationType'] as int, + userInvolved: map['userInvolved'] as UserEntity, + postInvolved: map['postInvolved'] as PostEntity, + timeStamp: map['timeStamp'] as int, + ); + } + + factory NotificationEntity.fromSnapshot(DocumentSnapshot snapshot) { + Map<String,dynamic> data = snapshot.data(); + return NotificationEntity( + notificationType: data['notificationType'] as int, + userInvolved: UserEntity.fromMap(data['userInvolved']), + postInvolved: PostEntity.fromMap(data['postInvolved']), + timeStamp: data['timeStamp'] as int, + ); + } + + + Map<String, dynamic> toMap() { + return { + 'notificationType': this.notificationType, + 'userInvolved': this.userInvolved.toMap(), + 'postInvolved': this.postInvolved.toMap(), + 'timeStamp': this.timeStamp, + }; + } + + +} \ No newline at end of file diff --git a/monumento_module/lib/resources/social/entities/post_entity.dart b/monumento_module/lib/resources/social/entities/post_entity.dart new file mode 100644 index 0000000..2f5dff3 --- /dev/null +++ b/monumento_module/lib/resources/social/entities/post_entity.dart @@ -0,0 +1,81 @@ +import 'dart:convert'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/authentication/entities/user_entity.dart'; + +class PostEntity { + final String postId; + final String imageUrl; + final String title; + final String location; + final int timeStamp; + final UserEntity author; + final String postByUid; + PostEntity( + {@required this.postId, + @required this.imageUrl, + @required this.title, + @required this.location, + @required this.timeStamp, + @required this.author,@required this.postByUid}); + + PostEntity copyWith( + {String postId, + String imageUrl, + String title, + String location, + int timeStamp, + UserEntity author,String postByUid}) { + return PostEntity( + postId: postId ?? this.postId, + imageUrl: imageUrl ?? this.imageUrl, + title: title ?? this.title, + location: location ?? this.location, + timeStamp: timeStamp ?? this.timeStamp, + author: author ?? this.author,postByUid: postByUid ?? this.postByUid); + } + + Map<String, dynamic> toMap() { + return { + 'postId': postId, + 'imageUrl': imageUrl, + 'title': title, + 'location': location, + 'timeStamp': timeStamp, + 'author': author.toMap(), + 'postByUid':postByUid + }; + } + + factory PostEntity.fromSnapshot(DocumentSnapshot docSnap) { + final Map<String, dynamic> data = docSnap.data(); + return PostEntity( + postId: docSnap.id, + imageUrl: data["imageUrl"], + title: data["title"], + location: data["location"], + timeStamp: data["timeStamp"], + author: UserEntity.fromMap(data["author"]),postByUid: data["postByUid"]); + } + + factory PostEntity.fromMap(Map<String, dynamic> map) { + return PostEntity( + postId: map['postId'], + imageUrl: map['imageUrl'], + title: map['title'], + location: map['location'], + timeStamp: map['timeStamp'], + author: UserEntity.fromMap(map['author']),postByUid: map['postByUid']); + } + + String toJson() => json.encode(toMap()); + + factory PostEntity.fromJson(String source) => + PostEntity.fromMap(json.decode(source)); + + @override + String toString() { + return 'PostEntity(postId: $postId, imageUrl: $imageUrl, title: $title, location: $location, timeStamp $timeStamp,, author: $author, postBYUid : $postByUid)'; + } +} diff --git a/monumento_module/lib/resources/social/firebase_social_repository.dart b/monumento_module/lib/resources/social/firebase_social_repository.dart new file mode 100644 index 0000000..fe9fa77 --- /dev/null +++ b/monumento_module/lib/resources/social/firebase_social_repository.dart @@ -0,0 +1,279 @@ +import 'dart:io'; + +import 'package:firebase_storage/firebase_storage.dart'; +import 'package:monumento/resources/authentication/authentication_repository.dart'; +import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/authentication/firebase_authentication_repository.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; +import 'package:monumento/resources/social/entities/comment_entity.dart'; +import 'package:monumento/resources/social/entities/notification_entity.dart'; +import 'package:monumento/resources/social/entities/post_entity.dart'; +import 'package:monumento/resources/social/models/comment_model.dart'; +import 'package:monumento/resources/social/models/notification_model.dart'; +import 'package:monumento/resources/social/models/post_model.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:monumento/resources/social/social_repository.dart'; +import 'package:uuid/uuid.dart'; + +class FirebaseSocialRepository implements SocialRepository { + final FirebaseFirestore _database; + final AuthenticationRepository _authRepository; + final FirebaseStorage _storage; + + FirebaseSocialRepository( + {FirebaseFirestore database, + AuthenticationRepository authenticationRepository, + FirebaseStorage storage}) + : _database = database ?? FirebaseFirestore.instance, + _storage = storage ?? FirebaseStorage.instance, + _authRepository = + authenticationRepository ?? FirebaseAuthenticationRepository(); + + @override + Future<List<PostModel>> getInitialFeedPosts() async { + UserModel currentUser = await _authRepository.getUser(); + //TODO TODO TODO + QuerySnapshot snap = await _database + .collection("posts") + // .where("postFor", arrayContains: currentUser.uid) + .orderBy("timeStamp", descending: true) + .limit(10) + .get(); + + List<PostModel> posts = snap.docs + .map((e) => PostModel.fromEntity( + entity: PostEntity.fromSnapshot(e), documentSnapshot: e)) + .toList(); + print("$posts lololol"); + return posts; + } + + @override + Future<List<PostModel>> getMorePosts({DocumentSnapshot startAfterDoc}) async { + UserModel currentUser = await _authRepository.getUser(); + + QuerySnapshot snap = await _database + .collection("posts") + // .where("postFor", arrayContains: currentUser.uid) + .orderBy("timeStamp", descending: true) + .startAfterDocument(startAfterDoc) + .limit(10) + .get(); + + List<PostModel> posts = snap.docs + .map((e) => PostModel.fromEntity( + entity: PostEntity.fromSnapshot(e), documentSnapshot: e)) + .toList(); + + return posts; + } + + @override + Future<PostModel> uploadNewPost( + {String title, String location, String imageUrl}) async { + int timeStamp = DateTime.now().millisecondsSinceEpoch; + UserModel user = await _authRepository.getUser(); + // TODO : postFor + DocumentReference ref = await _database.collection("posts").add({ + "title": title, + "location": location, + "imageUrl": imageUrl, + "timeStamp": timeStamp, + "author": user.toEntity().toMap() + // "postFor":[] + }); + DocumentSnapshot documentSnapshot = await ref.get(); + + return PostModel( + postId: ref.id, + imageUrl: imageUrl, + title: title, + location: location, + timeStamp: timeStamp, + documentSnapshot: documentSnapshot, + author: user); + } + + @override + Future<String> uploadImageForUrl({File file, String address}) async { + String fileName = Uuid().v4(); + UploadTask task = + _storage.ref().child(address).child("$fileName.jpg").putFile(file); + + TaskSnapshot snapshot = await task.whenComplete(() => null); + + return snapshot.ref.getDownloadURL(); + } + + @override + Future<String> uploadProfilePicForUrl({File file}) async { + final UserModel user = await _authRepository.getUser(); + String fileName = user.uid; + UploadTask task = _storage + .ref() + .child("profilePictures") + .child("$fileName.jpg") + .putFile(file); + + TaskSnapshot snapshot = await task.whenComplete(() => null); + + return snapshot.ref.getDownloadURL(); + } + + @override + Future<List<UserModel>> searchPeople({String searchQuery}) async { + // TODO: implement dateJoined field. + String query = searchQuery.toLowerCase().replaceAll(' ', ''); + QuerySnapshot snap = await _database + .collection("users") + .where("searchParams", arrayContains: query) + .limit(10) + .get(); + // .orderBy("dateJoined",descending: false) + List<UserModel> users = snap.docs + .map((e) => UserModel.fromEntity( + userEntity: UserEntity.fromSnapshot(e), snapshot: e)) + .toList(); + return users; + } + + @override + Future<List<UserModel>> getMoreSearchResults( + {String searchQuery, DocumentSnapshot<Object> startAfterDoc}) async { + QuerySnapshot snap = await _database + .collection("users") + .where("searchParams", arrayContains: searchQuery) + .orderBy("dateJoined", descending: false) + .startAfterDocument(startAfterDoc) + .limit(10) + .get(); + List<UserModel> users = snap.docs + .map((e) => UserModel.fromEntity( + userEntity: UserEntity.fromSnapshot(e), snapshot: e)) + .toList(); + return users; + } + + @override + Future<List<CommentModel>> getInitialComments( + {DocumentReference postDocReference}) async { + QuerySnapshot snap = await postDocReference + .collection("comments") + .orderBy("timeStamp", descending: true) + .limit(10) + .get(); + + return snap.docs + .map((e) => CommentModel.fromEntity( + entity: CommentEntity.fromSnapshot(e), snapshot: e)) + .toList(); + } + + @override + Future<List<NotificationModel>> getInitialNotifications() async { + UserModel user = await _authRepository.getUser(); + + QuerySnapshot snap = await user.documentSnapshot.reference + .collection("notifications") + .orderBy("timeStamp", descending: true) + .limit(10) + .get(); + + return snap.docs + .map((e) => NotificationModel.fromEntity( + entity: NotificationEntity.fromSnapshot(e), documentSnapshot: e)) + .toList(); + } + + @override + Future<List<CommentModel>> getMoreComments( + {DocumentReference postDocReference, + DocumentSnapshot startAfterDoc}) async { + QuerySnapshot snap = await postDocReference + .collection("comments") + .orderBy("timeStamp", descending: true) + .startAfterDocument(startAfterDoc) + .limit(10) + .get(); + + return snap.docs + .map((e) => CommentModel.fromEntity( + entity: CommentEntity.fromSnapshot(e), snapshot: e)) + .toList(); + } + + @override + Future<List<NotificationModel>> getMoreNotifications( + {DocumentSnapshot startAfterDoc}) async { + UserModel user = await _authRepository.getUser(); + QuerySnapshot snap = await user.documentSnapshot.reference + .collection("notifications") + .orderBy("timeStamp", descending: true) + .startAfterDocument(startAfterDoc) + .limit(10) + .get(); + + return snap.docs + .map((e) => NotificationModel.fromEntity( + entity: NotificationEntity.fromSnapshot(e), documentSnapshot: e)) + .toList(); + } + + @override + Future<CommentModel> addNewComment( + {DocumentReference postDocReference, String comment}) async { + UserModel user = await _authRepository.getUser(); + int timeStamp = DateTime.now().millisecondsSinceEpoch; + DocumentReference doc = await postDocReference.collection("comments").add({ + "comment": comment, + "timeStamp": timeStamp, + "postInvolvedId": postDocReference.id, + "author": { + "name": user.name, + "username": user.username, + "uid": user.uid, + "profilePictureUrl": user.profilePictureUrl, + "email": user.email + } + }); + return CommentModel( + comment: comment, + postInvolvedId: postDocReference.id, + author: user, + timeStamp: timeStamp); + } + + @override + Future<List<PostModel>> getInitialDiscoverPosts() async { + QuerySnapshot snap = await _database + .collection("posts") + .orderBy("timeStamp", descending: true) + .limit(10) + .get(); + + List<PostModel> posts = snap.docs + .map((e) => PostModel.fromEntity( + entity: PostEntity.fromSnapshot(e), documentSnapshot: e)) + .toList(); + print("$posts lololol"); + return posts; + } + + @override + Future<List<PostModel>> getMoreDiscoverPosts( + {DocumentSnapshot startAfterDoc}) async { + QuerySnapshot snap = await _database + .collection("posts") + .orderBy("timeStamp", descending: true) + .startAfterDocument(startAfterDoc) + .limit(10) + .get(); + + List<PostModel> posts = snap.docs + .map((e) => PostModel.fromEntity( + entity: PostEntity.fromSnapshot(e), documentSnapshot: e)) + .toList(); + + return posts; + } +} diff --git a/monumento_module/lib/resources/social/models/comment_model.dart b/monumento_module/lib/resources/social/models/comment_model.dart new file mode 100644 index 0000000..58bb42f --- /dev/null +++ b/monumento_module/lib/resources/social/models/comment_model.dart @@ -0,0 +1,84 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; +import 'package:monumento/resources/social/entities/comment_entity.dart'; + +class CommentModel { + final String comment; + final String postInvolvedId; + final UserModel author; + final int timeStamp; + final DocumentSnapshot snapshot; + +//<editor-fold desc="Data Methods" defaultstate="collapsed"> + + const CommentModel({ + @required this.comment, + @required this.postInvolvedId, + @required this.author, + @required this.timeStamp, + @required this.snapshot, + }); + + CommentModel copyWith({ + String comment, + String postInvolvedId, + UserModel author, + int timeStamp, + DocumentSnapshot snapshot, + }) { + return new CommentModel( + comment: comment ?? this.comment, + postInvolvedId: postInvolvedId ?? this.postInvolvedId, + author: author ?? this.author, + timeStamp: timeStamp ?? this.timeStamp, + snapshot: snapshot ?? this.snapshot, + ); + } + + @override + String toString() { + return 'CommentModel{comment: $comment, postInvolvedId: $postInvolvedId, author: $author, timeStamp: $timeStamp, snapshot: $snapshot}'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + (other is CommentModel && + runtimeType == other.runtimeType && + comment == other.comment && + postInvolvedId == other.postInvolvedId && + author == other.author && + timeStamp == other.timeStamp && + snapshot == other.snapshot); + + @override + int get hashCode => + comment.hashCode ^ + postInvolvedId.hashCode ^ + author.hashCode ^ + timeStamp.hashCode ^ + snapshot.hashCode; + + factory CommentModel.fromEntity( + {@required CommentEntity entity, DocumentSnapshot snapshot}) { + return new CommentModel( + comment: entity.comment, + postInvolvedId: entity.postInvolvedId, + author: UserModel.fromEntity(userEntity:entity.author), + timeStamp: entity.timeStamp, + snapshot: snapshot, + ); + } + + CommentEntity toEntity() { + return CommentEntity( + comment: comment, + postInvolvedId: postInvolvedId, + author: author.toEntity(), + timeStamp: timeStamp); + } + + +} diff --git a/monumento_module/lib/resources/social/models/notification_model.dart b/monumento_module/lib/resources/social/models/notification_model.dart new file mode 100644 index 0000000..37effc0 --- /dev/null +++ b/monumento_module/lib/resources/social/models/notification_model.dart @@ -0,0 +1,45 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; +import 'package:monumento/resources/social/entities/notification_entity.dart'; +import 'package:monumento/resources/social/models/post_model.dart'; + +class NotificationModel { + final NotificationType notificationType; + final UserModel userInvolved; + final PostModel postInvolved; + final int timeStamp; + final DocumentSnapshot documentSnapshot; + + NotificationModel( + {this.notificationType, + this.userInvolved, + this.postInvolved, + this.timeStamp, + this.documentSnapshot}); + + factory NotificationModel.fromEntity( + {@required NotificationEntity entity, DocumentSnapshot documentSnapshot}) { + return NotificationModel( + notificationType: NotificationType.values[entity.notificationType], + postInvolved: PostModel.fromEntity(entity: entity.postInvolved), + documentSnapshot: documentSnapshot); + } + + NotificationEntity toEntity() { + return NotificationEntity( + notificationType: notificationType.index, + timeStamp: timeStamp, + userInvolved:userInvolved.toEntity(), + postInvolved: (notificationType == NotificationType.likeNotification ||notificationType == NotificationType.commentNotification ) ? postInvolved.toEntity() : null, + ); + } +} + +enum NotificationType { + likeNotification, + commentNotification, + followRequest, + acceptedFollowRequested, + followedYou, +} diff --git a/monumento_module/lib/resources/social/models/post_model.dart b/monumento_module/lib/resources/social/models/post_model.dart new file mode 100644 index 0000000..5bc5fd6 --- /dev/null +++ b/monumento_module/lib/resources/social/models/post_model.dart @@ -0,0 +1,70 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; +import 'package:monumento/resources/social/entities/post_entity.dart'; + +class PostModel { + final String postId; + final String imageUrl; + final String title; + final String location; + final int timeStamp; + final DocumentSnapshot documentSnapshot; + final UserModel author; + final String postByUid; + + PostModel( + {@required this.postId, + @required this.imageUrl, + @required this.title, + @required this.location, + @required this.timeStamp, + @required this.documentSnapshot, + @required this.author,@required this.postByUid}); + + PostModel copyWith( + {String postId, + String imageUrl, + String title, + String location, + int timeStamp, + DocumentSnapshot documentSnapshot, + UserModel author,String postByUid}) { + return PostModel( + postId: postId ?? this.postId, + imageUrl: imageUrl ?? this.imageUrl, + title: title ?? this.title, + location: location ?? this.location, + timeStamp: timeStamp ?? this.timeStamp, + documentSnapshot: documentSnapshot ?? this.documentSnapshot, + author: author ?? this.author,postByUid: postByUid ?? this.postByUid); + } + + PostEntity toEntity() { + return PostEntity( + postId: postId, + imageUrl: imageUrl, + title: title, + location: location, + timeStamp: timeStamp, + author: author.toEntity()); + } + + factory PostModel.fromEntity( + {@required PostEntity entity, DocumentSnapshot documentSnapshot}) { + + return PostModel( + postId: entity.postId, + imageUrl: entity.imageUrl, + title: entity.title, + location: entity.location, + timeStamp: entity.timeStamp, + documentSnapshot: documentSnapshot, + author: UserModel.fromEntity(userEntity:entity.author),postByUid: entity.postByUid); + } + + @override + String toString() { + return 'PostModel(postId: $postId, imageUrl: $imageUrl, title: $title, location: $location, timeStamp: $timeStamp, author: $author, postByUid: $postByUid)'; + } +} diff --git a/monumento_module/lib/resources/social/social_repository.dart b/monumento_module/lib/resources/social/social_repository.dart new file mode 100644 index 0000000..f037237 --- /dev/null +++ b/monumento_module/lib/resources/social/social_repository.dart @@ -0,0 +1,53 @@ +import 'dart:io'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; +import 'package:monumento/resources/social/models/comment_model.dart'; +import 'package:monumento/resources/social/models/notification_model.dart'; + +import 'models/post_model.dart'; + +abstract class SocialRepository { + Future<List<PostModel>> getInitialFeedPosts(); + + Future<List<PostModel>> getMorePosts( + {@required DocumentSnapshot startAfterDoc}); + + Future<PostModel> uploadNewPost({@required String title, + @required String location, + @required String imageUrl}); + + Future<String> uploadImageForUrl( + {@required File file, @required String address}); + + Future<String> uploadProfilePicForUrl({@required File file}); + + Future<List<UserModel>> searchPeople({@required String searchQuery}); + + Future<List<UserModel>> getMoreSearchResults({@required String searchQuery, @required DocumentSnapshot startAfterDoc}); + + Future<CommentModel> addNewComment({@required DocumentReference postDocReference,@required String comment}); + + Future<List<CommentModel>> getInitialComments({@required DocumentReference postDocReference}); + + Future<List<CommentModel>> getMoreComments( + {@required DocumentReference postDocReference, @required DocumentSnapshot startAfterDoc}); + + + Future<List<NotificationModel>> getInitialNotifications(); + + Future<List<NotificationModel>> getMoreNotifications( + {@required DocumentSnapshot startAfterDoc}); + + Future<List<PostModel>> getInitialDiscoverPosts(); + + + Future<List<PostModel>> getMoreDiscoverPosts( + {@required DocumentSnapshot startAfterDoc}); + + + + + +} diff --git a/monumento_module/lib/screens/app_intro.dart b/monumento_module/lib/screens/app_intro.dart index b1b9ee7..9db6663 100644 --- a/monumento_module/lib/screens/app_intro.dart +++ b/monumento_module/lib/screens/app_intro.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:intro_views_flutter/Models/page_view_model.dart'; import 'package:intro_views_flutter/intro_views_flutter.dart'; import 'package:monumento/screens/login_screen.dart'; diff --git a/monumento_module/lib/screens/bookmark_screen.dart b/monumento_module/lib/screens/bookmark_screen.dart index 8035271..c35def2 100644 --- a/monumento_module/lib/screens/bookmark_screen.dart +++ b/monumento_module/lib/screens/bookmark_screen.dart @@ -18,11 +18,11 @@ class BookmarkScreen extends StatefulWidget { class _BookmarkScreenState extends State<BookmarkScreen> { Future<List<DocumentSnapshot>> getBookmarkedMonuments() async { - QuerySnapshot query = await Firestore.instance + QuerySnapshot query = await FirebaseFirestore.instance .collection('bookmarks') .where("uid", isEqualTo: widget.user.uid) - .getDocuments(); - return query.documents; + .get(); + return query.docs; } @override @@ -48,13 +48,16 @@ class _BookmarkScreenState extends State<BookmarkScreen> { return Container( child: Center( child: Text( - 'No Bookmarks!', - style: + 'No Bookmarks!', + style: TextStyle(fontWeight: FontWeight.w700, fontSize: 26.0), - )), + )), ); - widget.monumentList = snapshot.data.map((e) => BookmarkedMonumentModel.fromEntity(BookmarkedMonumentEntity.fromSnapshot(e))).toList(); + widget.monumentList = snapshot.data + .map((e) => BookmarkedMonumentModel.fromEntity( + BookmarkedMonumentEntity.fromSnapshot(e))) + .toList(); return ListView.builder( scrollDirection: Axis.vertical, itemCount: widget.monumentList.length, @@ -65,10 +68,11 @@ class _BookmarkScreenState extends State<BookmarkScreen> { context, MaterialPageRoute( builder: (_) => DetailScreen( - user: widget.user, - monument: widget.monumentList[index].monumentModel, - isBookMarked: true, - ))); + user: widget.user, + monument: widget + .monumentList[index].monumentModel, + isBookMarked: true, + ))); }, child: Card( margin: EdgeInsets.all(15.0), @@ -78,29 +82,30 @@ class _BookmarkScreenState extends State<BookmarkScreen> { ), child: Container( height: - MediaQuery.of(context).size.height * 0.2, + MediaQuery.of(context).size.height * 0.2, child: Row(children: <Widget>[ Hero( tag: widget.monumentList[index] - .monumentModel.wiki ?? + .monumentModel.wiki ?? 'monument-tag', child: Container( height: - MediaQuery.of(context).size.height * - 0.2, + MediaQuery.of(context).size.height * + 0.2, width: - MediaQuery.of(context).size.width * - 0.3, + MediaQuery.of(context).size.width * + 0.3, decoration: BoxDecoration( color: Colors.grey, borderRadius: BorderRadius.only( topLeft: Radius.circular(20.0), bottomLeft: - Radius.circular(20.0)), + Radius.circular(20.0)), image: DecorationImage( image: NetworkImage(widget .monumentList[index] - .monumentModel.imageUrl), + .monumentModel + .imageUrl), fit: BoxFit.cover)), )), SizedBox( @@ -110,53 +115,53 @@ class _BookmarkScreenState extends State<BookmarkScreen> { child: Container( child: Column( mainAxisAlignment: - MainAxisAlignment.spaceAround, + MainAxisAlignment.spaceAround, crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: <Widget>[ - Text( - widget - .monumentList[index].monumentModel.name, - style: TextStyle( - fontSize: 20.0, - fontWeight: FontWeight.w700), - ), - Container( - width: MediaQuery.of(context) - .size - .width * - 0.57, - child: Text( - widget.monumentList[index] - .monumentModel.city + - ', ' + - widget.monumentList[index] - .monumentModel.country, - maxLines: 3, - style: TextStyle( - fontSize: 18.0, - ), - overflow: TextOverflow.ellipsis, - ), - ), - Row( - children: <Widget>[ - Text( - 'Explore more', - style: TextStyle( - color: Colors.amber, - fontWeight: FontWeight.bold), - ), - SizedBox( - width: 6.0, - ), - Icon( - Icons.chevron_right, - color: Colors.amber, - ) - ], - ), - ]))) + Text( + widget.monumentList[index] + .monumentModel.name, + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w700), + ), + Container( + width: MediaQuery.of(context) + .size + .width * + 0.57, + child: Text( + widget.monumentList[index] + .monumentModel.city + + ', ' + + widget.monumentList[index] + .monumentModel.country, + maxLines: 3, + style: TextStyle( + fontSize: 18.0, + ), + overflow: TextOverflow.ellipsis, + ), + ), + Row( + children: <Widget>[ + Text( + 'Explore more', + style: TextStyle( + color: Colors.amber, + fontWeight: FontWeight.bold), + ), + SizedBox( + width: 6.0, + ), + Icon( + Icons.chevron_right, + color: Colors.amber, + ) + ], + ), + ]))) ])))); }); })); diff --git a/monumento_module/lib/screens/detail_screen.dart b/monumento_module/lib/screens/detail_screen.dart index ebc8bb8..73a9feb 100644 --- a/monumento_module/lib/screens/detail_screen.dart +++ b/monumento_module/lib/screens/detail_screen.dart @@ -21,7 +21,7 @@ class DetailScreen extends StatefulWidget { class _DetailScreenState extends State<DetailScreen> { final _key = GlobalKey<ScaffoldState>(); final Completer<WebViewController> _controller = - Completer<WebViewController>(); + Completer<WebViewController>(); Text _buildRatingStars(int rating) { String stars = ''; @@ -42,14 +42,16 @@ class _DetailScreenState extends State<DetailScreen> { Future<bool> getBookMarkStatus() async { String collection = "bookmarks"; - QuerySnapshot query = await Firestore.instance + QuerySnapshot query = await FirebaseFirestore.instance .collection(collection) .where("uid", isEqualTo: widget.user.uid) - .getDocuments(); - query.documents.forEach((element) { - if (element.data['name'] == widget.monument.name && - element.data['country'] == widget.monument.country && - element.data['city'] == widget.monument.city) + .get(); + query.docs.forEach((element) { + Map<String, dynamic> data = element.data(); + + if (data['name'] == widget.monument.name && + data['country'] == widget.monument.country && + data['city'] == widget.monument.city) setState(() { widget.isBookMarked = true; }); @@ -60,14 +62,16 @@ class _DetailScreenState extends State<DetailScreen> { Future<void> _delectbookmark() async { String collection = "bookmarks"; - QuerySnapshot query = await Firestore.instance + QuerySnapshot query = await FirebaseFirestore.instance .collection(collection) .where("uid", isEqualTo: widget.user.uid) - .getDocuments(); - query.documents.forEach((element) { - if (element.data['name'] == widget.monument.name && - element.data['country'] == widget.monument.country && - element.data['city'] == widget.monument.city) { + .get(); + query.docs.forEach((element) { + Map<String, dynamic> data = element.data(); + + if (data['name'] == widget.monument.name && + data['country'] == widget.monument.country && + data['city'] == widget.monument.city) { element.reference.delete(); } }); @@ -86,7 +90,7 @@ class _DetailScreenState extends State<DetailScreen> { _navToARFragment() async { List<Map<String, dynamic>> monumentMapList = new List(); - monumentMapList.add(widget.monument.toEntity().toDocument()); + monumentMapList.add(widget.monument.toEntity().toMap()); try { await platform .invokeMethod("navArFragment", {"monumentListMap": monumentMapList}); @@ -108,24 +112,21 @@ class _DetailScreenState extends State<DetailScreen> { map["city"] = widget.monument.city; DocumentReference documentReference = - Firestore.instance.collection(collection).document(); - Firestore.instance.runTransaction((transaction) async { - await transaction - .set(documentReference, map) - .catchError((e) {}) - .whenComplete(() { - setState(() { - widget.isBookMarked = true; - }); - print('Bookmarked!'); - _key.currentState.showSnackBar(SnackBar( - backgroundColor: Colors.amber, - content: Text( - 'Monument Bookmarked!', - style: TextStyle(color: Colors.white), - ), - )); + FirebaseFirestore.instance.collection(collection).doc(); + FirebaseFirestore.instance.runTransaction((transaction) async { + await transaction.set(documentReference, map); + // .catchError((e) {}) + setState(() { + widget.isBookMarked = true; }); + print('Bookmarked!'); + _key.currentState.showSnackBar(SnackBar( + backgroundColor: Colors.amber, + content: Text( + 'Monument Bookmarked!', + style: TextStyle(color: Colors.white), + ), + )); }).catchError((e) { print(e.toString()); }); @@ -183,7 +184,7 @@ class _DetailScreenState extends State<DetailScreen> { padding: EdgeInsets.only(right: 5.0), iconSize: 30.0, color: - widget.isBookMarked ? Colors.amber : Colors.white, + widget.isBookMarked ? Colors.amber : Colors.white, tooltip: 'Bookmark', onPressed: () async { if (!widget.isBookMarked) { @@ -266,30 +267,30 @@ class _DetailScreenState extends State<DetailScreen> { ), Expanded( child: IndexedStack( - index: _stackToView, - children: [ - Column( - children: <Widget>[ - Expanded( - child: WebView( - javascriptMode: JavascriptMode.unrestricted, - initialUrl: widget.monument.wiki, - gestureNavigationEnabled: true, - onWebViewCreated: (WebViewController webViewController) { - _controller.complete(webViewController); - }, - onPageFinished: _handleLoad, - )), + index: _stackToView, + children: [ + Column( + children: <Widget>[ + Expanded( + child: WebView( + javascriptMode: JavascriptMode.unrestricted, + initialUrl: widget.monument.wiki, + gestureNavigationEnabled: true, + onWebViewCreated: (WebViewController webViewController) { + _controller.complete(webViewController); + }, + onPageFinished: _handleLoad, + )), + ], + ), + Container( + color: Colors.white, + child: Center( + child: CircularProgressIndicator(), + ), + ), ], - ), - Container( - color: Colors.white, - child: Center( - child: CircularProgressIndicator(), - ), - ), - ], - )), + )), ], ), ); diff --git a/monumento_module/lib/screens/feed/feed_screen.dart b/monumento_module/lib/screens/feed/feed_screen.dart new file mode 100644 index 0000000..9618b48 --- /dev/null +++ b/monumento_module/lib/screens/feed/feed_screen.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + + +class FeedScreen extends StatefulWidget { + @override + _FeedScreenState createState() => _FeedScreenState(); +} + +class _FeedScreenState extends State<FeedScreen> { + + @override + Widget build(BuildContext context) { + return Center(child: Text("Feed Screen"),); + } + + +} diff --git a/monumento_module/lib/screens/home_screen.dart b/monumento_module/lib/screens/home_screen.dart index 1724082..df0feff 100644 --- a/monumento_module/lib/screens/home_screen.dart +++ b/monumento_module/lib/screens/home_screen.dart @@ -1,16 +1,17 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:monumento/blocs/bookmarked_monuments/bookmarked_monuments_bloc.dart'; -import 'package:monumento/blocs/popular_monuments/popular_monuments_bloc.dart'; import 'package:monumento/blocs/profile/profile_bloc.dart'; import 'package:monumento/resources/authentication/models/user_model.dart'; -import 'package:monumento/resources/monuments/models/monument_model.dart'; -import 'package:monumento/screens/bookmark_screen.dart'; -import 'package:monumento/screens/explore_screen.dart'; -import 'package:monumento/screens/profile_screen.dart'; -import 'package:monumento/utils/bookmark_carousel.dart'; -import 'package:monumento/utils/popular_carousel.dart'; +import 'package:monumento/screens/feed/feed_screen.dart'; + +import 'package:monumento/utils/image_picker.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + class HomeScreen extends StatefulWidget { final UserModel user; @@ -22,7 +23,6 @@ class HomeScreen extends StatefulWidget { } class _HomeScreenState extends State<HomeScreen> { - int _currentTab = 0; final _key = GlobalKey<ScaffoldState>(); List<Map<String, dynamic>> monumentMapList = new List(); @@ -40,10 +40,16 @@ class _HomeScreenState extends State<HomeScreen> { _profileBloc.add(GetProfileData(userId: uid)); } - void changeScreen(int tabIndex) { - setState(() { - _currentTab = tabIndex; - }); + int _currentIndex = 0; + + void onTabTapped(int newIndex) { + if (newIndex == 2) { + newPostBottomSheet(); + } else { + setState(() { + _currentIndex = newIndex; + }); + } } static const platform = const MethodChannel("monument_detector"); @@ -59,188 +65,75 @@ class _HomeScreenState extends State<HomeScreen> { @override Widget build(BuildContext context) { - return Scaffold( - key: _key, - body: _currentTab == 1 - ? BlocBuilder<PopularMonumentsBloc, PopularMonumentsState>( - builder: (context, state) { - if (state is PopularMonumentsRetrieved) { - return ExploreScreen( - user: widget.user, - monumentList: state.popularMonuments, - ); - } - return _buildCenterLoadingIndicator(); - }) - : _currentTab == 2 - ? BlocBuilder<BookmarkedMonumentsBloc, BookmarkedMonumentsState>( - builder: (context, state) { - if (state is BookmarkedMonumentsRetrieved) { - return BookmarkScreen( - user: widget.user, - monumentList: state.bookmarkedMonuments, - ); - } - return _buildCenterLoadingIndicator(); - }) - : _currentTab == 3 - ? BlocBuilder<ProfileBloc, ProfileState>( - builder: (context, profileState) { - return BlocBuilder<BookmarkedMonumentsBloc, - BookmarkedMonumentsState>( - builder: (context, bookmarkState) { - if (profileState is ProfileDataRetrieved && - bookmarkState is BookmarkedMonumentsRetrieved) { - return UserProfilePage( - user: widget.user, - bookmarkedMonuments: - bookmarkState.bookmarkedMonuments, - ); - } - return _buildCenterLoadingIndicator(); - }); - }) - : BlocBuilder<PopularMonumentsBloc, PopularMonumentsState>( - builder: (context, popularMonumentsState) { - if (popularMonumentsState is PopularMonumentsRetrieved) { - for (MonumentModel monument - in popularMonumentsState.popularMonuments) { - monumentMapList.add(monument.toEntity().toDocument()); - } - } - - return SafeArea( - child: (popularMonumentsState - is! PopularMonumentsRetrieved) - ? _buildCenterLoadingIndicator() - : Stack( - children: <Widget>[ - ListView( - padding: - EdgeInsets.symmetric(vertical: 30.0), - children: <Widget>[ - Padding( - padding: EdgeInsets.only( - left: 20.0, right: 120.0), - child: Text( - 'Monumento', - style: TextStyle( - fontSize: 28.0, - color: Colors.amber, - fontWeight: FontWeight.bold, - ), - ), - ), - SizedBox(height: 20.0), - PopularMonumentsCarousel( - popMonumentDocs: (popularMonumentsState - as PopularMonumentsRetrieved) - .popularMonuments, - user: widget.user, - changeTab: changeScreen, - ), - SizedBox(height: 20.0), - BlocBuilder<BookmarkedMonumentsBloc, - BookmarkedMonumentsState>( - builder: (context, state) { - if (state - is BookmarkedMonumentsRetrieved) { - return BookmarkCarousel( - bookmarkedMonumentDocs: - state.bookmarkedMonuments, - changeTab: changeScreen, - ); - } - - return SizedBox.shrink(); - }) - ], - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Align( - alignment: Alignment.bottomRight, - child: FloatingActionButton( - onPressed: () async { - _navToMonumentDetector(); - }, - backgroundColor: Colors.amber, - child: Icon(Icons.account_balance, - color: Colors.white), - )), - ) - ], - ), - ); - }), - bottomNavigationBar: BottomNavigationBar( - selectedLabelStyle: TextStyle(color: Colors.amber), - currentIndex: _currentTab, - elevation: 10.0, - selectedItemColor: Colors.amber, - onTap: (int value) { - setState(() { - _currentTab = value; - }); - }, - items: [ - BottomNavigationBarItem( - icon: Icon( - Icons.home, - size: 30.0, - color: Colors.grey, - ), - label: 'Home', - activeIcon: Icon( - Icons.home, - size: 35.0, - color: Colors.amber, - ), - ), - BottomNavigationBarItem( - icon: Icon( - Icons.apps, - size: 30.0, - color: Colors.grey, - ), - label: 'Popular', - activeIcon: Icon( - Icons.apps, - size: 35.0, - color: Colors.amber, - ), - ), - BottomNavigationBarItem( - icon: Icon( - Icons.bookmark, - size: 30.0, - color: Colors.grey, - ), - label: 'Bookmarks', - activeIcon: Icon( - Icons.bookmark, - size: 35.0, - color: Colors.amber, - ), - ), - BottomNavigationBarItem( - icon: Icon( - Icons.person_outline, - size: 30.0, - color: Colors.grey, - ), - label: 'Profile', - activeIcon: Icon( - Icons.person_outline, - size: 35.0, - color: Colors.amber, - ), - ), - ], + return SafeArea( + child: Scaffold( + + key: _key, + body: FeedScreen(), + floatingActionButton: FloatingActionButton(child: Icon(Icons.add_a_photo),onPressed: newPostBottomSheet,), + ), ); } + newPostBottomSheet() { + showModalBottomSheet( + context: context, + builder: (_) { + return Container( + padding: EdgeInsets.all(16), + height: 150, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "New Post", + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: <Widget>[ + Column( + children: [ + IconButton( + iconSize: 60, + icon: FaIcon( + FontAwesomeIcons.camera, + color: Colors.black, + ), + onPressed: () => + newPostPickImage(ImageSource.camera)), + Text("Camera") + ], + ), + Column( + children: [ + IconButton( + iconSize: 60, + icon: FaIcon( + FontAwesomeIcons.image, + color: Colors.black, + ), + onPressed: () => + newPostPickImage(ImageSource.gallery)), + Text("Gallery") + ], + ) + ], + ), + ], + ), + ); + }); + } + + Future newPostPickImage(ImageSource source) async { + File image = await PickImage.takePicture(imageSource: source); + File croppedImage = + await PickImage.cropImage(image: image, ratioX: 1, ratioY: 1); + Navigator.of(context).pushNamed('/newPostScreen', arguments: croppedImage); + } + Widget _buildCenterLoadingIndicator() { return Center( child: Container( diff --git a/monumento_module/lib/screens/new_post/new_post_screen.dart b/monumento_module/lib/screens/new_post/new_post_screen.dart new file mode 100644 index 0000000..aefd5a8 --- /dev/null +++ b/monumento_module/lib/screens/new_post/new_post_screen.dart @@ -0,0 +1,85 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:monumento/blocs/new_post/new_post_bloc.dart'; + +class NewPostScreen extends StatefulWidget { + @override + _NewPostScreenState createState() => _NewPostScreenState(); +} + +class _NewPostScreenState extends State<NewPostScreen> { + NewPostBloc _newPostBloc; + TextEditingController _titleController = TextEditingController(); + TextEditingController _locationController = TextEditingController(); + + @override + void initState() { + super.initState(); + _newPostBloc = BlocProvider.of<NewPostBloc>(context); + } + + @override + Widget build(BuildContext context) { + File pickedImage = ModalRoute.of(context).settings.arguments as File; + return BlocListener<NewPostBloc, NewPostState>( + listener: (context, state) { + if (state is NewPostAdded) { + + print("new post added"+ state.post.imageUrl); + } + }, + child: Scaffold( + appBar: AppBar( + title: Text("New Post"), + elevation: 0, + backgroundColor: Colors.white, + ), + persistentFooterButtons: [ + TextButton( + onPressed: () { + _newPostBloc.add(AddNewPost( + image: pickedImage, + location: _locationController.text, + title: _titleController.text)); + }, + child: Text("Post")) + ], + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Image.file(pickedImage, + height: MediaQuery.of(context).size.width, + width: MediaQuery.of(context).size.width), + ), + + + Divider( + height: 24, + thickness: 2, + ), + Text("Title"), + TextField( + controller: _titleController, + decoration: InputDecoration(hintText: "Add a title"), + ), + SizedBox(height: 16,), + Text("Location"), + TextField( + controller: _locationController, + decoration: InputDecoration(hintText: "Add a location"), + ), + ], + ), + ), + ), + )); + } +} diff --git a/monumento_module/lib/utils/image_picker.dart b/monumento_module/lib/utils/image_picker.dart new file mode 100644 index 0000000..53380ec --- /dev/null +++ b/monumento_module/lib/utils/image_picker.dart @@ -0,0 +1,38 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:image_cropper/image_cropper.dart'; +import 'package:image_picker/image_picker.dart'; + +class PickImage { + static Future<File> takePicture({ImageSource imageSource}) async { + try { + final _imagePicker = ImagePicker(); + final imagePickerFile = await _imagePicker.getImage( + source: imageSource, + ); + File imageFile = File(imagePickerFile.path); + if (imageFile == null) { + return null; + } + + return imageFile; + } catch (e) { + print(e.toString()); + } + } + + static Future<File> cropImage( + {@required File image, + @required double ratioX, + @required double ratioY}) async { + File croppedImageFile = await ImageCropper.cropImage( + sourcePath: image.path, + aspectRatio: CropAspectRatio(ratioX: ratioX, ratioY: ratioY), + compressQuality: 15); + if (croppedImageFile == null) { + return null; + } + print(croppedImageFile.path + " file name"); + return croppedImageFile; + } +} diff --git a/monumento_module/pubspec.yaml b/monumento_module/pubspec.yaml index aa88f5b..87de706 100644 --- a/monumento_module/pubspec.yaml +++ b/monumento_module/pubspec.yaml @@ -23,21 +23,26 @@ environment: dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. + cached_network_image: ^3.0.0 + cloud_firestore: null cupertino_icons: ^0.1.3 - firebase_auth: - cloud_firestore: - google_sign_in: - intro_views_flutter: - google_fonts: - webview_flutter: - google_maps_flutter: - geocoder: ^0.2.1 - flutter_bloc: ^7.0.0 equatable: ^2.0.0 + firebase_auth: null + firebase_storage: ^8.1.0 + flutter_bloc: ^7.0.0 + geocoder: ^0.2.1 + google_fonts: null + google_maps_flutter: null + google_sign_in: null + image_cropper: ^1.4.0 + image_picker: ^0.7.5+3 + intro_views_flutter: null + lazy_load_scrollview: ^1.3.0 meta: ^1.3.0 + uuid: ^3.0.4 + webview_flutter: null + convex_bottom_bar: ^3.0.0 + font_awesome_flutter: ^9.0.0 dev_dependencies: flutter_test: @@ -45,7 +50,6 @@ dev_dependencies: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec - flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in @@ -62,10 +66,8 @@ flutter: # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. - # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages - # To add Flutter specific custom fonts to your application, add a fonts # section here, in this "flutter" section. Each entry in this list should # have a "family" key with the font family name, and a "fonts" key with a @@ -85,7 +87,6 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages - # This section identifies your Flutter project as a module meant for # embedding in a native host app. These identifiers should _not_ ordinarily # be changed after generation - they are used to ensure that the tooling can -- GitLab From 524fdb3f6812ec0b3567fcb7ced2153cc39d0257 Mon Sep 17 00:00:00 2001 From: PaRaDoX50 <suryansh.stomar.min19@itbhu.ac.in> Date: Sat, 3 Jul 2021 01:09:36 +0530 Subject: [PATCH 2/2] Add the profile bloc --- app/.gitignore | 1 + monumento_module/.gitignore | 1 - .../lib/blocs/profile/profile_bloc.dart | 38 +++++++++++++++++++ .../lib/blocs/profile/profile_event.dart | 13 +++++++ .../lib/blocs/profile/profile_state.dart | 23 +++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 monumento_module/lib/blocs/profile/profile_bloc.dart create mode 100644 monumento_module/lib/blocs/profile/profile_event.dart create mode 100644 monumento_module/lib/blocs/profile/profile_state.dart diff --git a/app/.gitignore b/app/.gitignore index 796b96d..e464c60 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1 +1,2 @@ + /build diff --git a/monumento_module/.gitignore b/monumento_module/.gitignore index ff612b3..53171c1 100644 --- a/monumento_module/.gitignore +++ b/monumento_module/.gitignore @@ -10,7 +10,6 @@ .svn/ *.swp -profile DerivedData/ diff --git a/monumento_module/lib/blocs/profile/profile_bloc.dart b/monumento_module/lib/blocs/profile/profile_bloc.dart new file mode 100644 index 0000000..9dcaa5e --- /dev/null +++ b/monumento_module/lib/blocs/profile/profile_bloc.dart @@ -0,0 +1,38 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:meta/meta.dart'; +import 'package:monumento/resources/authentication/entities/user_entity.dart'; +import 'package:monumento/resources/authentication/models/user_model.dart'; +import 'package:monumento/resources/monuments/monument_repository.dart'; + +part 'profile_event.dart'; +part 'profile_state.dart'; + +class ProfileBloc extends Bloc<ProfileEvent, ProfileState> { + MonumentRepository _firebaseMonumentRepository; + ProfileBloc({@required MonumentRepository firebaseMonumentRepository}) + : assert(firebaseMonumentRepository != null), + _firebaseMonumentRepository = firebaseMonumentRepository, + super(ProfileInitial()); + + @override + Stream<ProfileState> mapEventToState( + ProfileEvent event, + ) async* { + if (event is GetProfileData) { + yield* _mapGetProfileDataToState(userId: event.userId); + } + } + + Stream<ProfileState> _mapGetProfileDataToState({String userId}) async* { + try { + final UserModel profileData = + await _firebaseMonumentRepository.getProfileData(userId); + yield ProfileDataRetrieved(profile: profileData); + } catch (_) { + yield FailedToRetrieveProfileData(); + } + } +} diff --git a/monumento_module/lib/blocs/profile/profile_event.dart b/monumento_module/lib/blocs/profile/profile_event.dart new file mode 100644 index 0000000..a89a500 --- /dev/null +++ b/monumento_module/lib/blocs/profile/profile_event.dart @@ -0,0 +1,13 @@ +part of 'profile_bloc.dart'; + +abstract class ProfileEvent extends Equatable { + const ProfileEvent(); +} + +class GetProfileData extends ProfileEvent { + final String userId; + GetProfileData({this.userId}); + @override + // TODO: implement props + List<Object> get props => [userId]; +} diff --git a/monumento_module/lib/blocs/profile/profile_state.dart b/monumento_module/lib/blocs/profile/profile_state.dart new file mode 100644 index 0000000..340ae21 --- /dev/null +++ b/monumento_module/lib/blocs/profile/profile_state.dart @@ -0,0 +1,23 @@ +part of 'profile_bloc.dart'; + +abstract class ProfileState extends Equatable { + const ProfileState(); +} + +class ProfileInitial extends ProfileState { + @override + List<Object> get props => []; +} + +class ProfileDataRetrieved extends ProfileState { + final UserModel profile; + + ProfileDataRetrieved({this.profile}); + @override + List<Object> get props => []; +} + +class FailedToRetrieveProfileData extends ProfileState { + @override + List<Object> get props => []; +} -- GitLab