cofounderella/lib/pages/liked_users_page.dart

369 lines
12 KiB
Dart
Raw Normal View History

2024-06-03 23:12:42 +02:00
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
2024-06-04 20:12:12 +02:00
import '../components/user_tile_likes.dart';
2024-06-03 23:12:42 +02:00
import '../constants.dart';
2024-06-06 16:11:26 +02:00
import '../enumerations.dart';
import '../models/swipe.dart';
2024-06-17 15:05:41 +02:00
import '../models/user_profile.dart';
2024-06-03 23:12:42 +02:00
import '../services/auth/auth_service.dart';
2024-06-17 15:05:41 +02:00
import '../services/user_service.dart';
2024-06-14 14:28:59 +02:00
import '../utils/helper_dialogs.dart';
import 'chat_page.dart';
import 'user_profile_page.dart';
2024-06-03 23:12:42 +02:00
class LikedUsersPage extends StatefulWidget {
const LikedUsersPage({super.key});
@override
LikedUsersPageState createState() => LikedUsersPageState();
}
class LikedUsersPageState extends State<LikedUsersPage> {
final String currentUserId = AuthService().getCurrentUser()!.uid;
2024-06-06 16:11:26 +02:00
final Map<String, bool> _cachedMatches = {};
List<MapEntry<Swipe, DocumentSnapshot>> _likedUsersWithSwipes = [];
bool _isLoading = true;
String? _fetchError;
2024-06-17 15:05:41 +02:00
UserProfile? _currentUser;
2024-06-03 23:12:42 +02:00
2024-06-04 20:12:12 +02:00
ViewOrder _orderPreference = ViewOrder.swipedFirst;
MenuSort _sortPreference = MenuSort.nameAsc;
2024-06-06 16:11:26 +02:00
@override
void initState() {
super.initState();
2024-06-17 15:05:41 +02:00
_loadCurrentUserProfile();
2024-06-06 16:11:26 +02:00
_fetchLikedUsersWithSwipes();
}
2024-06-03 23:12:42 +02:00
2024-06-17 15:05:41 +02:00
Future<void> _loadCurrentUserProfile() async {
try {
_currentUser = await UserService.getUserProfileById(currentUserId);
} catch (e) {
debugPrint("Error loading current user profile: $e");
}
}
2024-06-06 16:11:26 +02:00
Future<void> _fetchLikedUsersWithSwipes() async {
try {
QuerySnapshot likedUsersSnapshot = await FirebaseFirestore.instance
2024-06-03 23:12:42 +02:00
.collection(Constants.dbCollectionUsers)
2024-06-06 16:11:26 +02:00
.doc(currentUserId)
.collection(Constants.dbCollectionSwipes)
.where(Constants.dbFieldSwipesLike, isEqualTo: true)
2024-06-03 23:12:42 +02:00
.get();
2024-06-06 16:11:26 +02:00
List<MapEntry<Swipe, DocumentSnapshot>> likedUsersWithSwipes = [];
for (var doc in likedUsersSnapshot.docs) {
Swipe swipe = Swipe.fromDocument(doc);
DocumentSnapshot userDoc = await FirebaseFirestore.instance
.collection(Constants.dbCollectionUsers)
.doc(swipe.swipedId)
.get();
// Initialize _cachedMatches to keep database accesses to a minimum
2024-06-17 20:39:56 +02:00
bool hasMatch =
await UserService.hasMatch(currentUserId, swipe.swipedId);
2024-06-06 16:11:26 +02:00
_cachedMatches[swipe.swipedId] = hasMatch;
likedUsersWithSwipes.add(MapEntry(swipe, userDoc));
}
setState(() {
_likedUsersWithSwipes = likedUsersWithSwipes;
_isLoading = false;
});
} catch (e) {
setState(() {
_fetchError = e.toString();
_isLoading = false;
});
}
2024-06-03 23:12:42 +02:00
}
Future<void> _unlikeUser(String userId) async {
try {
await FirebaseFirestore.instance
.collection(Constants.dbCollectionUsers)
.doc(currentUserId)
.collection(Constants.dbCollectionSwipes)
.doc(userId)
2024-06-06 16:11:26 +02:00
.delete();
// Refresh the UI
setState(() {
_likedUsersWithSwipes
.removeWhere((entry) => entry.key.swipedId == userId);
});
2024-06-03 23:12:42 +02:00
} catch (e) {
2024-06-06 16:11:26 +02:00
if (mounted) {
2024-06-17 20:39:56 +02:00
showMsg(context, 'Error undoing Like', e.toString());
2024-06-06 16:11:26 +02:00
}
2024-06-03 23:12:42 +02:00
}
}
2024-06-06 16:11:26 +02:00
Future<bool?> _showConfirmationDialog(String userId, String userName) async {
2024-06-03 23:12:42 +02:00
bool? confirm = await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
2024-06-14 14:28:59 +02:00
title: const Text('Confirm Removal'),
content:
Text('Are you sure you want to remove $userName from your list?'),
2024-06-03 23:12:42 +02:00
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Confirm'),
),
],
);
},
);
2024-06-06 16:11:26 +02:00
return confirm;
2024-06-03 23:12:42 +02:00
}
2024-06-06 16:11:26 +02:00
List<MapEntry<Swipe, DocumentSnapshot>> _getSortedLikedUsersWithSwipes() {
List<MapEntry<Swipe, DocumentSnapshot>> likedOnlyUsers = [];
List<MapEntry<Swipe, DocumentSnapshot>> matchedUsers = [];
for (var entry in _likedUsersWithSwipes) {
bool hasMatch = _cachedMatches.containsKey(entry.key.swipedId)
? _cachedMatches[entry.key.swipedId]!
: false;
2024-06-04 20:12:12 +02:00
if (hasMatch) {
2024-06-06 16:11:26 +02:00
matchedUsers.add(entry);
2024-06-04 20:12:12 +02:00
} else {
2024-06-06 16:11:26 +02:00
likedOnlyUsers.add(entry);
2024-06-04 20:12:12 +02:00
}
}
2024-06-06 16:11:26 +02:00
if (_sortPreference == MenuSort.timestampAsc) {
likedOnlyUsers.sort((a, b) => a.key.timestamp.compareTo(b.key.timestamp));
matchedUsers.sort((a, b) => a.key.timestamp.compareTo(b.key.timestamp));
} else if (_sortPreference == MenuSort.timestampDesc) {
likedOnlyUsers.sort((a, b) => b.key.timestamp.compareTo(a.key.timestamp));
matchedUsers.sort((a, b) => b.key.timestamp.compareTo(a.key.timestamp));
} else if (_sortPreference == MenuSort.nameAsc) {
likedOnlyUsers.sort((a, b) => _compareNames(a, b));
matchedUsers.sort((a, b) => _compareNames(a, b));
2024-06-04 20:12:12 +02:00
} else if (_sortPreference == MenuSort.nameDesc) {
2024-06-06 16:11:26 +02:00
likedOnlyUsers.sort((a, b) => _compareNames(b, a));
matchedUsers.sort((a, b) => _compareNames(b, a));
2024-06-04 20:12:12 +02:00
}
2024-06-06 16:11:26 +02:00
if (_orderPreference == ViewOrder.matchedFirst) {
2024-06-04 20:12:12 +02:00
return [...matchedUsers, ...likedOnlyUsers];
2024-06-06 16:11:26 +02:00
} else if (_orderPreference == ViewOrder.swipedOnly) {
return [...likedOnlyUsers];
} else if (_orderPreference == ViewOrder.matchedOnly) {
return [...matchedUsers];
} else {
// default: swipesFirst
return [...likedOnlyUsers, ...matchedUsers];
2024-06-04 20:12:12 +02:00
}
}
2024-06-06 16:11:26 +02:00
int _compareNames(MapEntry<Swipe, DocumentSnapshot> a,
MapEntry<Swipe, DocumentSnapshot> b) {
final dataA = (a.value.data() as Map<String, dynamic>);
final dataB = (b.value.data() as Map<String, dynamic>);
final nameA =
dataA.isNotEmpty && dataA.containsKey(Constants.dbFieldUsersName)
? a.value[Constants.dbFieldUsersName]
: '';
final nameB =
dataB.isNotEmpty && dataB.containsKey(Constants.dbFieldUsersName)
? b.value[Constants.dbFieldUsersName]
: '';
return nameA.compareTo(nameB);
}
2024-06-03 23:12:42 +02:00
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My favored profiles'),
2024-06-03 23:12:42 +02:00
),
2024-06-04 20:12:12 +02:00
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DropdownButton<ViewOrder>(
value: _orderPreference,
items: const [
DropdownMenuItem(
2024-06-06 16:11:26 +02:00
value: ViewOrder.swipedFirst,
child: Text('Swipes First'),
),
DropdownMenuItem(
value: ViewOrder.matchedFirst,
child: Text('Matches First'),
),
2024-06-04 20:12:12 +02:00
DropdownMenuItem(
2024-06-06 16:11:26 +02:00
value: ViewOrder.swipedOnly,
child: Text('Swipes Only'),
),
DropdownMenuItem(
value: ViewOrder.matchedOnly,
child: Text('Matches Only'),
),
2024-06-04 20:12:12 +02:00
],
onChanged: (value) {
// update UI on change only
if (_orderPreference != value) {
setState(() {
_orderPreference = value!;
});
}
},
),
Align(
alignment: Alignment.centerRight,
child: PopupMenuButton<MenuSort>(
icon: const Icon(Icons.sort),
onSelected: (MenuSort item) {
// update UI on change only
if (_sortPreference != item) {
setState(() {
_sortPreference = item;
});
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<MenuSort>>[
PopupMenuItem<MenuSort>(
value: MenuSort.nameAsc,
child: ListTile(
leading: _sortPreference == MenuSort.nameAsc
? const Icon(Icons.check)
: const Icon(null),
title: const Text('Name Ascending'),
),
),
PopupMenuItem<MenuSort>(
value: MenuSort.nameDesc,
child: ListTile(
leading: _sortPreference == MenuSort.nameDesc
? const Icon(Icons.check)
: const Icon(null),
title: const Text('Name Descending'),
),
),
2024-06-06 16:11:26 +02:00
PopupMenuItem<MenuSort>(
value: MenuSort.timestampAsc,
child: ListTile(
leading: _sortPreference == MenuSort.timestampAsc
? const Icon(Icons.check)
: const Icon(null),
title: const Text('Oldest first'),
),
),
PopupMenuItem<MenuSort>(
value: MenuSort.timestampDesc,
child: ListTile(
leading: _sortPreference == MenuSort.timestampDesc
? const Icon(Icons.check)
: const Icon(null),
title: const Text('Newest first'),
),
),
2024-06-04 20:12:12 +02:00
],
),
),
],
),
),
2024-06-06 16:11:26 +02:00
_isLoading
? const Center(child: CircularProgressIndicator())
: _fetchError != null
? Center(child: Text('Error: $_fetchError'))
: _likedUsersWithSwipes.isEmpty
2024-06-17 17:57:32 +02:00
? const Center(child: Text('No favored profiles yet.'))
2024-06-06 16:11:26 +02:00
: buildLikedUserList(),
2024-06-04 20:12:12 +02:00
],
),
);
}
Widget buildLikedUserList() {
2024-06-06 16:11:26 +02:00
var sortedSwipeUsers = _getSortedLikedUsersWithSwipes();
2024-06-04 20:12:12 +02:00
return Expanded(
2024-06-06 16:11:26 +02:00
child: ListView.builder(
itemCount: sortedSwipeUsers.length,
itemBuilder: (BuildContext context, int index) {
var entry = sortedSwipeUsers[index];
var user = entry.value;
bool hasMatch = _cachedMatches.containsKey(user.id)
? _cachedMatches[user.id]!
: false;
return UserTileLikes(
user: user,
hasMatch: hasMatch,
2024-06-17 17:57:32 +02:00
currentUser: _currentUser,
2024-06-06 16:11:26 +02:00
onUnlike: () async {
Map<String, dynamic> userMap =
user.data() as Map<String, dynamic>;
bool hasName = userMap.containsKey(Constants.dbFieldUsersName);
bool? confirm = await _showConfirmationDialog(
user.id,
(hasName ? user[Constants.dbFieldUsersName] : 'Name: n/a'),
);
if (confirm == true) {
2024-06-17 20:39:56 +02:00
bool hasMatch =
await UserService.hasMatch(currentUserId, user.id);
2024-06-06 16:11:26 +02:00
if (!hasMatch) {
await _unlikeUser(user.id);
} else {
// abort, update _cachedMatches and UI
_cachedMatches[user.id] = hasMatch;
setState(() {});
}
}
},
onShowChat: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatPage(
receiverEmail: user[Constants.dbFieldUsersEmail],
receiverID: user[Constants.dbFieldUsersID],
chatTitle: user[Constants.dbFieldUsersName],
2024-06-16 01:41:42 +02:00
profileImageUrl: user[Constants.dbFieldUsersProfilePic],
),
),
);
},
onViewInfo: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => UserProfilePage(
userId: user.id,
),
),
);
},
2024-06-06 16:11:26 +02:00
);
2024-06-03 23:12:42 +02:00
},
),
);
}
}