diff --git a/lib/components/my_drawer.dart b/lib/components/my_drawer.dart index f70557a..d4f2201 100644 --- a/lib/components/my_drawer.dart +++ b/lib/components/my_drawer.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../pages/conversations_page.dart'; import '../pages/home_page.dart'; +import '../pages/liked_users_page.dart'; import '../pages/user_data_page.dart'; import '../pages/settings_page.dart'; import '../pages/user_matching_page.dart'; @@ -76,6 +77,27 @@ class MyDrawer extends StatelessWidget { ), ), + // liked users tile + Padding( + padding: const EdgeInsets.only(left: 25), + child: ListTile( + title: const Text('Swiped Users List'), + leading: const Icon(Icons.format_list_bulleted), + onTap: () { + // pop the drawer + Navigator.pop(context); + // Navigate to HomePage? + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => + const LikedUsersPage(), + ), + ); + }, + ), + ), + // chats list tile Padding( padding: const EdgeInsets.only(left: 25), diff --git a/lib/pages/liked_users_page.dart b/lib/pages/liked_users_page.dart new file mode 100644 index 0000000..e863496 --- /dev/null +++ b/lib/pages/liked_users_page.dart @@ -0,0 +1,258 @@ +import 'package:flutter/material.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; + +import '../constants.dart'; +import '../services/auth/auth_service.dart'; +import '../utils/helper.dart'; + +class LikedUsersPage extends StatefulWidget { + const LikedUsersPage({super.key}); + + @override + LikedUsersPageState createState() => LikedUsersPageState(); +} + +class LikedUsersPageState extends State { + final String currentUserId = AuthService().getCurrentUser()!.uid; + + Future> _fetchLikedUsers() async { + QuerySnapshot likedUsersSnapshot = await FirebaseFirestore.instance + .collection(Constants.dbCollectionUsers) + .doc(currentUserId) + .collection(Constants.dbCollectionSwipes) + .where('liked', isEqualTo: true) + .get(); + + List likedUserIds = + likedUsersSnapshot.docs.map((doc) => doc.id).toList(); + + List likedUsers = []; + for (String userId in likedUserIds) { + DocumentSnapshot userDoc = await FirebaseFirestore.instance + .collection(Constants.dbCollectionUsers) + .doc(userId) + .get(); + likedUsers.add(userDoc); + } + + return likedUsers; + } + + Future _hasMatch(String userId) async { + DocumentSnapshot matchDoc = await FirebaseFirestore.instance + .collection(Constants.dbCollectionUsers) + .doc(currentUserId) + .collection(Constants.dbCollectionMatches) + .doc(userId) + .get(); + return matchDoc.exists; + } + + Future _unlikeUser(String userId) async { + try { + await FirebaseFirestore.instance + .collection(Constants.dbCollectionUsers) + .doc(currentUserId) + .collection(Constants.dbCollectionSwipes) + .doc(userId) + .delete(); //.update({'liked': false}); + setState(() {}); // Refresh the UI + } catch (e) { + showMsg(context, 'Error unlike', e.toString()); + } + } + + void _showMatchMessage() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Match Exists'), + content: const Text( + 'You cannot unlike a user with whom you have a match.', + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Close'), + ), + ], + ); + }, + ); + } + + Future _showConfirmationDialog(String userId, String userName) async { + bool? confirm = await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Confirm Unlike'), + content: Text('Are you sure you want to unlike $userName?'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Confirm'), + ), + ], + ); + }, + ); + + if (confirm == true) { + await _unlikeUser(userId); + } + } + + void _showUserInfo(DocumentSnapshot user) { + Map userMap = user.data() as Map; + bool hasName = userMap.containsKey(Constants.dbFieldUsersName); + bool hasBio = userMap.containsKey(Constants.dbFieldUsersBio); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: hasName + ? Text(user[Constants.dbFieldUsersName]) + : const Text('Name: n/a'), + content: hasBio + ? Text(user[Constants.dbFieldUsersBio]) + : const Text('Bio: n/a'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Close'), + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Liked Users'), + ), + body: FutureBuilder>( + future: _fetchLikedUsers(), + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } else if (!snapshot.hasData || snapshot.data!.isEmpty) { + return const Center(child: Text('No liked users found.')); + } else { + return ListView.builder( + itemCount: snapshot.data!.length, + itemBuilder: (BuildContext context, int index) { + DocumentSnapshot user = snapshot.data![index]; + return FutureBuilder( + future: _hasMatch(user.id), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const CircularProgressIndicator(); + } + bool hasMatch = snapshot.data ?? false; + return UserInfoTile( + user: user, + hasMatch: hasMatch, + onUnlike: () { + Map userMap = + user.data() as Map; + bool hasName = + userMap.containsKey(Constants.dbFieldUsersName); + + _showConfirmationDialog( + user.id, + (hasName + ? user[Constants.dbFieldUsersName] + : 'Name: n/a'), + ); + }, //_unlikeUser(user.id), + onShowMatchMessage: _showMatchMessage, + onViewInfo: () => _showUserInfo(user), + ); + }, + ); + }, + ); + } + }, + ), + ); + } +} + +class UserInfoTile extends StatelessWidget { + final DocumentSnapshot user; + final bool hasMatch; + final VoidCallback onUnlike; + final VoidCallback onShowMatchMessage; + final VoidCallback onViewInfo; + + const UserInfoTile({ + super.key, + required this.user, + required this.hasMatch, + required this.onUnlike, + required this.onShowMatchMessage, + required this.onViewInfo, + }); + + @override + Widget build(BuildContext context) { + Map userMap = user.data() as Map; + bool hasPictureUrl = userMap.containsKey(Constants.dbFieldUsersProfilePic); + bool hasName = userMap.containsKey(Constants.dbFieldUsersName); + bool hasBio = userMap.containsKey(Constants.dbFieldUsersBio); + + return Card( + margin: const EdgeInsets.all(8.0), + child: ListTile( + leading: hasPictureUrl == true && + user[Constants.dbFieldUsersProfilePic] != null + ? CircleAvatar( + backgroundImage: + NetworkImage(user[Constants.dbFieldUsersProfilePic]), + ) + : const CircleAvatar(child: Icon(Icons.person)), + title: hasName + ? Text( + '${user[Constants.dbFieldUsersName]} ${user[Constants.dbFieldUsersName]} ${user[Constants.dbFieldUsersName]}', + overflow: TextOverflow.ellipsis, + maxLines: 1, + ) + : null, + subtitle: hasBio + ? Text( + user[Constants.dbFieldUsersBio], + overflow: TextOverflow.ellipsis, + maxLines: 3, + ) + : null, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.contact_page_outlined), + onPressed: onViewInfo, + ), + IconButton( + icon: Icon(hasMatch ? Icons.lock_outline : Icons.delete_outline), + onPressed: hasMatch ? onShowMatchMessage : onUnlike, + ), + ], + ), + ), + ); + } +}