import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import '../components/user_tile_likes.dart'; import '../constants.dart'; import '../services/auth/auth_service.dart'; import '../utils/helper.dart'; enum MenuSort { nameAsc, nameDesc, timestampAsc, timestampDesc } enum ViewOrder { swipedFirst, matchedFirst } class LikedUsersPage extends StatefulWidget { const LikedUsersPage({super.key}); @override LikedUsersPageState createState() => LikedUsersPageState(); } class LikedUsersPageState extends State { final String currentUserId = AuthService().getCurrentUser()!.uid; ViewOrder _orderPreference = ViewOrder.swipedFirst; MenuSort _sortPreference = MenuSort.nameAsc; 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'), ), ], ); }, ); } Future> _fetchSortedLikedUsers() async { List likedUsers = await _fetchLikedUsers(); List likedOnlyUsers = []; List matchedUsers = []; for (DocumentSnapshot user in likedUsers) { bool hasMatch = await _hasMatch(user.id); if (hasMatch) { matchedUsers.add(user); } else { likedOnlyUsers.add(user); } } if (_sortPreference == MenuSort.nameAsc) { likedOnlyUsers.sort((a, b) => (a[Constants.dbFieldUsersName] as String) .compareTo(b[Constants.dbFieldUsersName] as String)); matchedUsers.sort((a, b) => (a[Constants.dbFieldUsersName] as String) .compareTo(b[Constants.dbFieldUsersName] as String)); } else if (_sortPreference == MenuSort.nameDesc) { likedOnlyUsers.sort((a, b) => (b[Constants.dbFieldUsersName] as String) .compareTo(a[Constants.dbFieldUsersName] as String)); matchedUsers.sort((a, b) => (b[Constants.dbFieldUsersName] as String) .compareTo(a[Constants.dbFieldUsersName] as String)); } if (_orderPreference == ViewOrder.swipedFirst) { return [...likedOnlyUsers, ...matchedUsers]; } else { return [...matchedUsers, ...likedOnlyUsers]; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Liked Users'), ), body: Column( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ DropdownButton( value: _orderPreference, items: const [ DropdownMenuItem( value: ViewOrder.swipedFirst, child: Text('Swiped First')), DropdownMenuItem( value: ViewOrder.matchedFirst, child: Text('Matched First')), ], onChanged: (value) { // update UI on change only if (_orderPreference != value) { setState(() { _orderPreference = value!; }); } }, ), Align( alignment: Alignment.centerRight, child: PopupMenuButton( icon: const Icon(Icons.sort), onSelected: (MenuSort item) { // update UI on change only if (_sortPreference != item) { setState(() { _sortPreference = item; }); } }, itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: MenuSort.nameAsc, child: ListTile( leading: _sortPreference == MenuSort.nameAsc ? const Icon(Icons.check) : const Icon(null), title: const Text('Name Ascending'), ), ), PopupMenuItem( value: MenuSort.nameDesc, child: ListTile( leading: _sortPreference == MenuSort.nameDesc ? const Icon(Icons.check) : const Icon(null), title: const Text('Name Descending'), ), ), ], ), ), ], ), ), buildLikedUserList(), ], ), ); } Widget buildLikedUserList() { return Expanded( child: FutureBuilder>( future: _fetchSortedLikedUsers(), 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 UserTileLikes( 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), ); }, ); }, ); } }, ), ); } }