Added ConversationsPage
parent
97863bff2d
commit
cad532da0b
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../pages/conversations_page.dart';
|
||||||
import '../pages/home_page.dart';
|
import '../pages/home_page.dart';
|
||||||
import '../pages/user_data_page.dart';
|
import '../pages/user_data_page.dart';
|
||||||
import '../pages/settings_page.dart';
|
import '../pages/settings_page.dart';
|
||||||
|
@ -38,7 +39,7 @@ class MyDrawer extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 25),
|
padding: const EdgeInsets.only(left: 25),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: const Text("Home"),
|
title: const Text('Home'),
|
||||||
leading: const Icon(Icons.home),
|
leading: const Icon(Icons.home),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// pop the drawer
|
// pop the drawer
|
||||||
|
@ -58,7 +59,7 @@ class MyDrawer extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 25),
|
padding: const EdgeInsets.only(left: 25),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: const Text("Find Matches"),
|
title: const Text('Find Matches'),
|
||||||
leading: const Icon(Icons.person_search),
|
leading: const Icon(Icons.person_search),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// pop the drawer
|
// pop the drawer
|
||||||
|
@ -79,9 +80,17 @@ class MyDrawer extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 25),
|
padding: const EdgeInsets.only(left: 25),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: const Text("Conversations"),
|
title: const Text('Chats'),
|
||||||
leading: const Icon(Icons.chat),
|
leading: const Icon(Icons.chat),
|
||||||
onTap: () {}, // TODO
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ConversationsPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -107,7 +116,7 @@ class MyDrawer extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 25),
|
padding: const EdgeInsets.only(left: 25),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: const Text("My Profile Settings"),
|
title: const Text('My Profile Settings'),
|
||||||
leading: const Icon(Icons.edit_note),
|
leading: const Icon(Icons.edit_note),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// pop the drawer first, then navigate to destination
|
// pop the drawer first, then navigate to destination
|
||||||
|
@ -156,7 +165,7 @@ class MyDrawer extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 25),
|
padding: const EdgeInsets.only(left: 25),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: const Text("About the app"),
|
title: const Text('About the app'),
|
||||||
leading: const Icon(Icons.perm_device_info),
|
leading: const Icon(Icons.perm_device_info),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
@ -171,7 +180,7 @@ class MyDrawer extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 25),
|
padding: const EdgeInsets.only(left: 25),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: const Text("Send feedback"),
|
title: const Text('Send feedback'),
|
||||||
leading: const Icon(Icons.sentiment_satisfied_alt),
|
leading: const Icon(Icons.sentiment_satisfied_alt),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
|
@ -187,7 +196,7 @@ class MyDrawer extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 25, bottom: 25),
|
padding: const EdgeInsets.only(left: 25, bottom: 25),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: const Text("L O G O U T"),
|
title: const Text('L O G O U T'),
|
||||||
leading: const Icon(Icons.logout),
|
leading: const Icon(Icons.logout),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../components/user_tile.dart';
|
||||||
|
import '../constants.dart';
|
||||||
|
import '../services/auth/auth_service.dart';
|
||||||
|
import '../services/user_service.dart';
|
||||||
|
import 'chat_page.dart';
|
||||||
|
|
||||||
|
class ConversationsPage extends StatelessWidget {
|
||||||
|
ConversationsPage({super.key});
|
||||||
|
|
||||||
|
// auth service
|
||||||
|
final AuthService _authService = AuthService();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Conversations'),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
foregroundColor: Colors.grey.shade800,
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
body: _buildUserMatchesList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the list of matches for the current user
|
||||||
|
Widget _buildUserMatchesList() {
|
||||||
|
final currentUser = _authService.getCurrentUser();
|
||||||
|
|
||||||
|
if (currentUser == null) {
|
||||||
|
return const Center(child: Text('Error: User not logged in'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return StreamBuilder<List<Map<String, dynamic>>>(
|
||||||
|
stream: UserService.getMatchedUsersStream(currentUser.uid),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
// error
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Text('Error: ${snapshot.error.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
// loading
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
// return list view
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
final matchedUsers = snapshot.data!;
|
||||||
|
if (matchedUsers.isEmpty) {
|
||||||
|
return const Center(child: Text('No matches yet'));
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
children: matchedUsers
|
||||||
|
.map<Widget>(
|
||||||
|
(userData) => _buildUserListItem(userData, context))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(child: Text('No matches found'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build individual user list item
|
||||||
|
Widget _buildUserListItem(
|
||||||
|
Map<String, dynamic> userData, BuildContext context) {
|
||||||
|
return UserTile(
|
||||||
|
text: userData[Constants.dbFieldUsersEmail],
|
||||||
|
onTap: () {
|
||||||
|
// tapped on a user -> go to chat page
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ChatPage(
|
||||||
|
receiverEmail: userData[Constants.dbFieldUsersEmail],
|
||||||
|
receiverID: userData[Constants.dbFieldUsersID],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +1,22 @@
|
||||||
import 'package:cofounderella/components/my_drawer.dart';
|
|
||||||
import 'package:cofounderella/components/user_tile.dart';
|
|
||||||
import 'package:cofounderella/services/auth/auth_service.dart';
|
|
||||||
import 'package:cofounderella/services/chat/chat_service.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../components/my_drawer.dart';
|
||||||
|
import '../components/user_tile.dart';
|
||||||
|
import '../constants.dart';
|
||||||
|
import '../services/auth/auth_service.dart';
|
||||||
|
import '../services/user_service.dart';
|
||||||
import 'chat_page.dart';
|
import 'chat_page.dart';
|
||||||
|
|
||||||
class HomePage extends StatelessWidget {
|
class HomePage extends StatelessWidget {
|
||||||
HomePage({super.key});
|
HomePage({super.key});
|
||||||
|
|
||||||
// chat and auth service
|
|
||||||
final ChatService _chatService = ChatService();
|
|
||||||
final AuthService _authService = AuthService();
|
final AuthService _authService = AuthService();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Home"),
|
title: const Text('Home'),
|
||||||
// TODO appbar style: remove background and set elevation to 0 ?
|
// TODO appbar style: remove background and set elevation to 0 ?
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: Colors.grey.shade800,
|
foregroundColor: Colors.grey.shade800,
|
||||||
|
@ -31,16 +30,16 @@ class HomePage extends StatelessWidget {
|
||||||
// build a list of users except for the current logged in user
|
// build a list of users except for the current logged in user
|
||||||
Widget _buildUserList() {
|
Widget _buildUserList() {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
stream: _chatService.getUsersStream(),
|
stream: UserService.getUsersStream(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
// error
|
// error
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return const Text("Error");
|
return Text('Error: ${snapshot.error.toString()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return const Text("Loading..");
|
return const Text('Loading..');
|
||||||
}
|
}
|
||||||
|
|
||||||
// return list view
|
// return list view
|
||||||
|
@ -57,17 +56,18 @@ class HomePage extends StatelessWidget {
|
||||||
Widget _buildUserListItem(
|
Widget _buildUserListItem(
|
||||||
Map<String, dynamic> userData, BuildContext context) {
|
Map<String, dynamic> userData, BuildContext context) {
|
||||||
// display all users except current user
|
// display all users except current user
|
||||||
if (userData["email"] != _authService.getCurrentUser()!.email) {
|
if (userData[Constants.dbFieldUsersEmail] !=
|
||||||
|
_authService.getCurrentUser()!.email) {
|
||||||
return UserTile(
|
return UserTile(
|
||||||
text: userData["email"],
|
text: userData[Constants.dbFieldUsersEmail],
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// tapped on a user -> go to chat page // TODO
|
// tapped on a user -> go to chat page
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => ChatPage(
|
builder: (context) => ChatPage(
|
||||||
receiverEmail: userData["email"],
|
receiverEmail: userData[Constants.dbFieldUsersEmail],
|
||||||
receiverID: userData["uid"],
|
receiverID: userData[Constants.dbFieldUsersID],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,8 +31,11 @@ class RegisterPage extends StatelessWidget {
|
||||||
.signUpWithEmailPassword(
|
.signUpWithEmailPassword(
|
||||||
_emailController.text, _passwordController.text)
|
_emailController.text, _passwordController.text)
|
||||||
.then((userCredential) {
|
.then((userCredential) {
|
||||||
UserService.saveUserData(userCredential, _emailController.text,
|
UserService.saveUserRegistrationData(
|
||||||
_nameController.text, _lastnameController.text);
|
userCredential,
|
||||||
|
_emailController.text,
|
||||||
|
_nameController.text,
|
||||||
|
_lastnameController.text);
|
||||||
});
|
});
|
||||||
} on FirebaseAuthException catch (e) {
|
} on FirebaseAuthException catch (e) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
|
|
@ -10,22 +10,6 @@ class ChatService {
|
||||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||||
final FirebaseAuth _auth = FirebaseAuth.instance;
|
final FirebaseAuth _auth = FirebaseAuth.instance;
|
||||||
|
|
||||||
// get user stream
|
|
||||||
Stream<List<Map<String, dynamic>>> getUsersStream() {
|
|
||||||
return _firestore
|
|
||||||
.collection(Constants.dbCollectionUsers)
|
|
||||||
.snapshots()
|
|
||||||
.map((snapshot) {
|
|
||||||
return snapshot.docs.map((doc) {
|
|
||||||
// iterate each user
|
|
||||||
final user = doc.data();
|
|
||||||
|
|
||||||
//return user
|
|
||||||
return user;
|
|
||||||
}).toList();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// send message
|
// send message
|
||||||
Future<void> sendMessage(String receiverID, message) async {
|
Future<void> sendMessage(String receiverID, message) async {
|
||||||
// get current user info
|
// get current user info
|
||||||
|
@ -61,7 +45,7 @@ class ChatService {
|
||||||
.collection(Constants.dbCollectionChatRooms)
|
.collection(Constants.dbCollectionChatRooms)
|
||||||
.doc(chatRoomID)
|
.doc(chatRoomID)
|
||||||
.collection(Constants.dbCollectionMessages)
|
.collection(Constants.dbCollectionMessages)
|
||||||
.orderBy("timestamp", descending: false)
|
.orderBy('timestamp', descending: false)
|
||||||
.snapshots();
|
.snapshots();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ import '../models/user_profile.dart';
|
||||||
class UserService {
|
class UserService {
|
||||||
UserService._(); // Private constructor to prevent instantiation
|
UserService._(); // Private constructor to prevent instantiation
|
||||||
|
|
||||||
static Future<void> saveUserData(UserCredential userCredential, String email,
|
static Future<void> saveUserRegistrationData(UserCredential userCredential,
|
||||||
String firstname, String lastname) async {
|
String email, String firstname, String lastname) async {
|
||||||
// create full name
|
// create full name
|
||||||
String fullName = (firstname.isNotEmpty && lastname.isNotEmpty)
|
String fullName = (firstname.isNotEmpty && lastname.isNotEmpty)
|
||||||
? '$firstname $lastname'
|
? '$firstname $lastname'
|
||||||
|
@ -151,4 +151,71 @@ class UserService {
|
||||||
|
|
||||||
return userProfile;
|
return userProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get users stream
|
||||||
|
static Stream<List<Map<String, dynamic>>> getUsersStream() {
|
||||||
|
return FirebaseFirestore.instance
|
||||||
|
.collection(Constants.dbCollectionUsers)
|
||||||
|
.snapshots()
|
||||||
|
.map((snapshot) {
|
||||||
|
return snapshot.docs.map((doc) {
|
||||||
|
// iterate each user
|
||||||
|
final user = doc.data();
|
||||||
|
|
||||||
|
//return user
|
||||||
|
return user;
|
||||||
|
}).toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get list of matched user ids for a given user
|
||||||
|
static Future<List<String>> getMatchedUserIds(String userId) async {
|
||||||
|
List<String> matchedUserIds = [];
|
||||||
|
|
||||||
|
final snapshot = await FirebaseFirestore.instance
|
||||||
|
.collection(Constants.dbCollectionUsers)
|
||||||
|
.doc(userId)
|
||||||
|
.collection(Constants.dbCollectionMatches)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
for (var doc in snapshot.docs) {
|
||||||
|
final data = doc.data();
|
||||||
|
final otherUserId = data['otherUserId'] as String?;
|
||||||
|
if (otherUserId != null && otherUserId.isNotEmpty) {
|
||||||
|
matchedUserIds.add(otherUserId);
|
||||||
|
}
|
||||||
|
// assuming the match document ID is the matched user's ID
|
||||||
|
// it would just be an one liner:
|
||||||
|
// matchedUserIds.add(doc.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedUserIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get matched users data for a given user as stream
|
||||||
|
static Stream<List<Map<String, dynamic>>> getMatchedUsersStream(
|
||||||
|
String userId) {
|
||||||
|
return FirebaseFirestore.instance
|
||||||
|
.collection(Constants.dbCollectionUsers)
|
||||||
|
.doc(userId)
|
||||||
|
.collection(Constants.dbCollectionMatches)
|
||||||
|
.snapshots()
|
||||||
|
.asyncMap((snapshot) async {
|
||||||
|
final matchedUserIds = snapshot.docs
|
||||||
|
.map((doc) => doc.data()['otherUserId'] as String?)
|
||||||
|
.where((otherUserId) => otherUserId != null && otherUserId.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (matchedUserIds.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final matchedUsersSnapshot = await FirebaseFirestore.instance
|
||||||
|
.collection(Constants.dbCollectionUsers)
|
||||||
|
.where(FieldPath.documentId, whereIn: matchedUserIds)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
return matchedUsersSnapshot.docs.map((doc) => doc.data()).toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue