diff --git a/neighborcafe_app/lib/src/components/bottom_navigation_bar.dart b/neighborcafe_app/lib/src/components/bottom_navigation_bar.dart index 457b5fe..8430681 100644 --- a/neighborcafe_app/lib/src/components/bottom_navigation_bar.dart +++ b/neighborcafe_app/lib/src/components/bottom_navigation_bar.dart @@ -24,6 +24,10 @@ class BottomNavBar extends StatelessWidget { icon: Icon(Icons.coffee_outlined), label: 'CoffeeBot', ), + BottomNavigationBarItem( + icon: Icon(Icons.favorite_border), + label: 'Favoritos', + ), BottomNavigationBarItem( icon: Icon(Icons.shopping_bag_outlined), label: 'Tiendas', diff --git a/neighborcafe_app/lib/src/components/main_wrapper.dart b/neighborcafe_app/lib/src/components/main_wrapper.dart index 04ce597..b2ccd85 100644 --- a/neighborcafe_app/lib/src/components/main_wrapper.dart +++ b/neighborcafe_app/lib/src/components/main_wrapper.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:neighborcafe/src/views/private/favorites_view.dart'; import 'bottom_navigation_bar.dart'; import '../views/private/map_view.dart'; import '../views/private/recommendations_view.dart'; @@ -31,12 +32,14 @@ class _MainScreenWrapperState extends State { final List screens = const [ MapView(), RecommendationsView(), + FavoritesView(), StoresView(), ]; final List titles = const [ 'Mapa', 'CoffeeBot', + 'Favoritos', 'Tiendas', ]; @@ -65,11 +68,18 @@ class _MainScreenWrapperState extends State { @override Widget build(BuildContext context) { return ExitConfirmationWrapper( - isDrawerOpen: () => _scaffoldKey.currentState?.isEndDrawerOpen ?? false, + isDrawerOpen: () => _scaffoldKey.currentState?.isDrawerOpen ?? false, child: Scaffold( key: _scaffoldKey, appBar: AppBar( automaticallyImplyLeading: false, + leading: Builder( + builder: (context) => IconButton( + icon: const Icon(Icons.menu), + onPressed: () => _scaffoldKey.currentState?.openDrawer(), + color: Colors.white, + ), + ), title: Row( children: [ Image.asset( @@ -82,18 +92,8 @@ class _MainScreenWrapperState extends State { fontWeight: FontWeight.bold, fontSize: 20.0)), ], ), - actions: [ - Builder( - builder: (context) => IconButton( - icon: const Icon(Icons.menu), - // onPressed: () => Scaffold.of(context).openEndDrawer(), - onPressed: () => _scaffoldKey.currentState?.openEndDrawer(), - color: Colors.white, - ), - ), - ], ), - endDrawer: Drawer( + drawer: Drawer( child: Container( color: AppColors.backgroundColor, child: ListView( diff --git a/neighborcafe_app/lib/src/views/private/favorites_view.dart b/neighborcafe_app/lib/src/views/private/favorites_view.dart new file mode 100644 index 0000000..58ffd7f --- /dev/null +++ b/neighborcafe_app/lib/src/views/private/favorites_view.dart @@ -0,0 +1,173 @@ +import 'package:flutter/material.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:logger/logger.dart'; +import 'package:neighborcafe/src/settings/app_colors.dart'; + +class FavoritesView extends StatefulWidget { + const FavoritesView({super.key}); + + @override + FavoritesViewState createState() => FavoritesViewState(); +} + +class FavoritesViewState extends State { + final _auth = FirebaseAuth.instance; + final _firestore = FirebaseFirestore.instance; + final Logger _logger = Logger(); + + User? loggedinUser; + String? username; + + @override + void initState() { + super.initState(); + getCurrentUser(); + } + + void getCurrentUser() async { + final user = _auth.currentUser; // No es necesario usar await aquí + if (user != null) { + setState(() { + loggedinUser = user; // Actualiza el estado + }); + await getUsername(user.uid); + } else { + // Manejar caso donde el usuario no está autenticado + _logger.e("No user is currently logged in."); + } + } + + // Función para obtener el nombre de usuario desde Firestore + Future getUsername(String uid) async { + try { + DocumentSnapshot userDoc = + await _firestore.collection('users').doc(uid).get(); + if (userDoc.exists) { + setState(() { + username = userDoc['name']; // Almacenar el nombre de usuario + }); + } + } catch (e) { + _logger.e('Error getting username: $e'); + } + } + + Stream>> getFavorites(String? username) { + return FirebaseFirestore.instance + .collection('favorites') + .where('user', isEqualTo: username) + .snapshots() + .map((snapshot) => snapshot.docs.map((doc) { + final data = doc.data(); + data['id'] = doc.id; + return data; + }).toList()); +} + + Future deleteFavorite(String docId) async { + await FirebaseFirestore.instance.collection('favorites').doc(docId).delete(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + // Título en la parte superior + const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + 'Mis Cafeterías Favoritas', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: AppColors.primaryColor, + ), + ), + ), + Expanded( + child: StreamBuilder>>( + stream: getFavorites(username), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (!snapshot.hasData || snapshot.data!.isEmpty) { + return const Center( + child: Text('No tienes cafeterías favoritas aún.'), + ); + } + + final favorites = snapshot.data!; + + return ListView.builder( + itemCount: favorites.length, + itemBuilder: (context, index) { + final favorite = favorites[index]; + return Card( + margin: const EdgeInsets.symmetric( + vertical: 8.0, horizontal: 16.0), + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: ListTile( + leading: favorite['place_photoUrl'] != null + ? ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: Image.network( + favorite['place_photoUrl'], + width: 50, + height: 50, + fit: BoxFit.cover, + ), + ) + : const Icon(Icons.coffee, size: 50), + title: Text( + favorite['place_name'] ?? 'Nombre no disponible', + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(favorite['place_address'] ?? 'Dirección no disponible'), + trailing: IconButton( + icon: const Icon(Icons.delete, color: Colors.red), + onPressed: () async { + final confirm = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Eliminar favorito'), + content: const Text( + '¿Estás seguro de que deseas eliminar esta cafetería de tus favoritos?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('Cancelar'), + ), + TextButton( + onPressed: () => Navigator.pop(context, true), + child: const Text('Eliminar'), + ), + ], + ), + ); + + if (confirm == true) { + await deleteFavorite(favorite['id']); + } + }, + ), + ), + ); + }, + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/neighborcafe_app/lib/src/views/private/map_view.dart b/neighborcafe_app/lib/src/views/private/map_view.dart index 9b86643..1a92f32 100644 --- a/neighborcafe_app/lib/src/views/private/map_view.dart +++ b/neighborcafe_app/lib/src/views/private/map_view.dart @@ -36,6 +36,7 @@ class MapViewState extends State { final Location _location = Location(); final Set _markers = {}; + @override void initState() { super.initState(); @@ -199,6 +200,28 @@ class MapViewState extends State { } } + Future handleFavorite(Map place, String? photoUrl) async { + final favoritesCollection = _firestore.collection('favorites'); + final querySnapshot = await favoritesCollection + .where('user', isEqualTo: username) + .where('place_id', isEqualTo: place["place_id"]) + .get(); + + if (querySnapshot.docs.isEmpty) { + await favoritesCollection.add({ + 'place_address': place['vicinity'], + 'place_id': place["place_id"], + 'place_name': place['name'], + 'place_photoUrl': photoUrl, + 'user': username, + }); + } else { + for (var doc in querySnapshot.docs) { + await doc.reference.delete(); + } + } + } + void _showMarkerDetails(Map place) async { final apiKey = await _getApiKey(); String imageUrl; @@ -288,12 +311,39 @@ class MapViewState extends State { ), const SizedBox(height: 8.0), Center( - child: Text( - place['name'] ?? 'No name available', - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: AppColors.secondaryColor), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + place['name'] ?? 'No name available', + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: AppColors.secondaryColor, + ), + ), + const SizedBox(width: 8.0), + StreamBuilder( + stream: FirebaseFirestore.instance + .collection('favorites') + .where('user', isEqualTo: username) + .where('place_id', isEqualTo: place['place_id']) + .snapshots(), + builder: (context, snapshot) { + final isFavorite = snapshot.hasData && snapshot.data!.docs.isNotEmpty; + + return IconButton( + icon: Icon( + isFavorite ? Icons.favorite : Icons.favorite_border, + color: isFavorite ? Colors.red : Colors.grey, + ), + onPressed: () { + handleFavorite(place, imageUrl); + }, + ); + }, + ), + ], ), ), const SizedBox(height: 8.0),