From 24f0d80e32df6c874e3a70828c5acc6bbc33afc2 Mon Sep 17 00:00:00 2001 From: Felipe Date: Sat, 11 Nov 2023 18:13:58 -0500 Subject: [PATCH] update --- lib/constansts.dart | 77 +- lib/main.dart | 2 +- .../authentication_repository.dart | 3 +- lib/src/controllers/otp_controller.dart | 3 +- lib/src/models/setting_model.dart | 57 +- lib/src/presentation/screens/map/service.dart | 1025 +++++++++++++++++ .../screens/professional_profile.dart | 86 +- .../presentation/screens/profile/profile.dart | 290 +++-- .../presentation/screens/service_after.dart | 2 +- .../{service.dart => service_old.dart} | 34 +- .../widgets/shared/drawer_menu.dart | 9 +- lib/src/providers/user_provider.dart | 14 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 24 + pubspec.yaml | 5 +- 15 files changed, 1439 insertions(+), 194 deletions(-) create mode 100644 lib/src/presentation/screens/map/service.dart rename lib/src/presentation/screens/{service.dart => service_old.dart} (98%) diff --git a/lib/constansts.dart b/lib/constansts.dart index dbefbc2..cc041a5 100644 --- a/lib/constansts.dart +++ b/lib/constansts.dart @@ -1 +1,76 @@ -const String apiKey = 'AIzaSyBqZNw7kj3pEYH-pusqTMxXhbja8HR7eOc'; +const String GOOGLE_MAPS_API_KEY = 'AIzaSyCW_og6qQ8W8G-5_BxIS4sBnl8cLkjL95s'; + +const String MAP_STYLE = ''' + [ + { + "elementType": "labels.icon", + "stylers": [ + { + "visibility": "on", + "color": "#6F6F6F" + } + ] + }, + { + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#616161" + } + ] + }, + { + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#f5f5f5" + } + ] + }, + { + "featureType": "administrative.land_parcel", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#bdbdbd" + } + ] + }, + { + "featureType": "poi", + "elementType": "geometry", + "stylers": [ + { + "color": "#eeeeee" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry", + "stylers": [ + { + "color": "#e5e5e5" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9e9e9e" + } + ] + } +] + '''; diff --git a/lib/main.dart b/lib/main.dart index 79705c0..72673ce 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,7 +21,7 @@ import 'package:prosappco/src/presentation/screens/profile/profile_pro.dart'; import 'package:prosappco/src/presentation/screens/register/register.dart'; import 'package:prosappco/src/presentation/screens/request_sent.dart'; import 'package:prosappco/src/presentation/screens/reset_password/reset_password.dart'; -import 'package:prosappco/src/presentation/screens/service.dart'; +import 'package:prosappco/src/presentation/screens/map/service.dart'; import 'package:prosappco/src/presentation/screens/solicitudes.dart'; import 'package:prosappco/src/services/local_notifications.dart'; import 'firebase_options.dart'; diff --git a/lib/src/authentication/authentication_repository.dart b/lib/src/authentication/authentication_repository.dart index aa29fc5..743da19 100644 --- a/lib/src/authentication/authentication_repository.dart +++ b/lib/src/authentication/authentication_repository.dart @@ -4,7 +4,8 @@ import 'package:get/get.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:prosappco/src/authentication/exceptions/register_failed.dart'; import 'package:prosappco/src/presentation/screens/login/login.dart'; -import 'package:prosappco/src/presentation/screens/service.dart'; +// import 'package:prosappco/src/presentation/screens/service.dart'; +import 'package:prosappco/src/presentation/screens/map/service.dart'; class AuthenticationRepository extends GetxController { static AuthenticationRepository get instance => Get.find(); diff --git a/lib/src/controllers/otp_controller.dart b/lib/src/controllers/otp_controller.dart index 3b894da..87883be 100644 --- a/lib/src/controllers/otp_controller.dart +++ b/lib/src/controllers/otp_controller.dart @@ -1,6 +1,7 @@ import 'package:get/get.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/presentation/screens/service.dart'; +// import 'package:prosappco/src/presentation/screens/service.dart'; +import 'package:prosappco/src/presentation/screens/map/service.dart'; class OTPController extends GetxController { static OTPController get instance => Get.find(); diff --git a/lib/src/models/setting_model.dart b/lib/src/models/setting_model.dart index a94a320..2869680 100644 --- a/lib/src/models/setting_model.dart +++ b/lib/src/models/setting_model.dart @@ -29,30 +29,55 @@ class SettingModel { this.terminosCondiciones, ); - static Future fromJson(Map json) async { + static Future fromJson(Map? json) async { try { - if (json == null) + if (json == null) { return SettingModel( - false, false, false, '', '', '', '', '', '', '', '', ''); + false, + false, + false, + '', + '', + '', + '', + '', + '', + '', + '', + '', + ); + } return SettingModel( - json['domicilio'], - json['google'], - json['tarifa'], - json['titulo_soporte'], - json['parrafo_soporte'], - json['numero_soporte'], - json['email_soporte'], - json['dias_soporte'], - json['horas_soporte'], - json['version'], - json['politicas_privacidad'], - json['terminos_condiciones'], + json['domicilios'] ?? false, + json['google'] ?? false, + json['tarifas'] ?? false, + json['titulo_soporte'] ?? '', + json['parrafo_soporte'] ?? '', + json['numero_soporte'] ?? '', + json['email_soporte'] ?? '', + json['dias_soporte'] ?? '', + json['horas_soporte'] ?? '', + json['version'] ?? '', // Asegúrate de manejar nulos aquí + json['politicas_privacidad'] ?? '', + json['terminos_condiciones'] ?? '', ); } catch (e) { print('Error settings: $e'); return SettingModel( - false, false, false, '', '', '', '', '', '', '', '', ''); + false, + false, + false, + '', + '', + '', + '', + '', + '', + '', + '', + '', + ); } } diff --git a/lib/src/presentation/screens/map/service.dart b/lib/src/presentation/screens/map/service.dart new file mode 100644 index 0000000..ac4c678 --- /dev/null +++ b/lib/src/presentation/screens/map/service.dart @@ -0,0 +1,1025 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:geocoding/geocoding.dart' as geocoding; +import 'package:http/http.dart' as http; +import 'package:geolocator/geolocator.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:flutter_polyline_points/flutter_polyline_points.dart'; +import 'package:intl/intl.dart'; +import 'package:location/location.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:prosappco/constansts.dart'; +import 'package:prosappco/src/authentication/authentication_repository.dart'; +import 'package:prosappco/src/components/network_utility.dart'; +import 'package:prosappco/src/models/event_model.dart'; +import 'package:prosappco/src/models/setting_model.dart'; +import 'package:prosappco/src/models/user_model.dart'; +import 'package:prosappco/src/presentation/screens/professional.dart'; +import 'package:prosappco/src/presentation/screens/profile/profile.dart'; +import 'package:prosappco/src/presentation/screens/service_after.dart'; +import 'package:prosappco/src/presentation/screens/service_type.dart'; +import 'package:prosappco/src/presentation/widgets/shared/drawer_menu.dart'; +import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; +import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ServiceScreen extends StatefulWidget { + const ServiceScreen({super.key}); + + @override + State createState() => _ServiceScreenState(); +} + +class _ServiceScreenState extends State { + late String appVersion; + final Uri _url = Uri.parse( + 'https://play.google.com/store/apps/details?id=com.prosapp.prosapp'); + + final Location _locationController = Location(); + + final Completer _mapController = + Completer(); + + static const LatLng _pGooglePlex = LatLng(7.080486, -73.087447); + static const LatLng _pApplePark = LatLng(7.074710, -73.090807); + LatLng? _currentP; + + Map polylines = {}; + + final TextEditingController _locacionController = TextEditingController(); + final TextEditingController _ubicationController = TextEditingController(); + final TextEditingController _profesionalController = TextEditingController(); + final TextEditingController _serviceTypeController = TextEditingController(); + final TextEditingController _observacionController = TextEditingController(); + final DateTime now = DateTime.now(); + EventoService eventoService = EventoService(); + String _serviceType = 'Servicio'; + String ubicacion = ''; + String professionalId = ''; + String professionalToken = ''; + String professionalAddress = ''; + String professionalUbicacion = ''; + int? professionalTarifa; + double? professionalLatitude; + double? professionalLongitude; + DateTime? _selectedDate; + + String _ciudad = '...'; + + TimeOfDay? _selectedTime; + final DateFormat formatter = DateFormat('dd/MM/yyyy'); + dynamic coordinates; + Set markers = {}; + BitmapDescriptor? _markerIcon; + List _placesList = []; + String _coordsOfCity = '0.0,0.0'; + + final uid = AuthenticationRepository.instance.getCurrentUserUid(); + + UserModel? user; + String? settings; + + @override + void initState() { + super.initState(); + + if (user == null) { + UserModel.getUser(uid.toString()).then( + (UserModel s) => (value) { + if (mounted) { + setState(() => user = s); + } + }, + ); + } + + _getAppVersion(); + + getLocationUpdates().then( + (_) => { + getPolylinePoints().then( + (coordinates) => { + generatePolyLineFromPoints(coordinates), + }, + ), + }, + ); + + if (settings == null) { + SettingModel.getSettings().then( + (SettingModel value) => (value) { + if (mounted) { + setState(() { + settings = value.version; + _getAppVersion(); + }); + } + }, + ); + } + + if (_ciudad == '...') { + AuthenticationRepository.instance + .getCity(uid.toString()) + .then((String s) { + if (s.isEmpty) { + FirebaseFirestore.instance.collection('users').doc(uid).set({ + 'city': 'Cúcuta', + }).then((_) { + if (mounted) { + setState(() { + _ciudad = 'Cúcuta'; + }); + } + }); + } else { + if (mounted) { + setState(() { + _ciudad = s; + }); + } + } + }); + } + + if (_coordsOfCity == '0.0,0.0') { + AuthenticationRepository.instance + .getCoordsOfCity(uid.toString()) + .then((String s) { + if (mounted) { + setState(() { + _coordsOfCity = s; + + _animateInitialCameraToPosition(_coordsOfCity); + }); + } + }); + } + + _saveToken(); + + BitmapDescriptor.fromAssetImage( + const ImageConfiguration(size: Size(2, 2)), 'images/pro_marke.png') + .then((icon) { + if (mounted) { + setState(() { + _markerIcon = icon; + }); + } + }); + + updateMarkersForServiceType(_serviceType); + } + + @override + void dispose() { + _locacionController.dispose(); + _ubicationController.dispose(); + _profesionalController.dispose(); + _serviceTypeController.dispose(); + _observacionController.dispose(); + + _locationController.onLocationChanged.listen((_) {}).cancel(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + drawer: DrawerMenu(), + body: Column( + children: [ + Expanded( + child: _currentP == null + ? const Center(child: CircularProgressIndicator()) + : Stack( + children: [ + GoogleMap( + onMapCreated: ((GoogleMapController controller) { + _mapController.complete(controller); + + _applyMapStyle(controller); + }), + initialCameraPosition: const CameraPosition( + target: _pGooglePlex, + zoom: 15, + ), + markers: { + ...markers, + Marker( + markerId: const MarkerId("_currentLocation"), + icon: BitmapDescriptor.defaultMarker, + position: _currentP!, + ), + const Marker( + markerId: MarkerId("_sourceLocation"), + icon: BitmapDescriptor.defaultMarker, + position: _pGooglePlex, + ), + const Marker( + markerId: MarkerId("_destionationLocation"), + icon: BitmapDescriptor.defaultMarker, + position: _pApplePark, + ), + }, + polylines: Set.of(polylines.values), + onCameraIdle: () { + if (professionalAddress == '') { + if (coordinates != null) { + getLocationName(coordinates.latitude, + coordinates.longitude) + .then((locationName) { + if (mounted) { + setState(() { + _locacionController.text = locationName; + }); + } + }); + } + } + }, + onCameraMove: (position) { + if (mounted) { + setState(() { + coordinates = position.target; + }); + } + }, + ), + Positioned( + top: 10, + left: 10, + child: Builder( + builder: (context) { + return ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + shape: const CircleBorder(), + elevation: 3, + minimumSize: const Size(50, 50), + ), + child: const Icon( + Icons.menu, + color: Colors.black, + size: 35, + ), + onPressed: () { + Scaffold.of(context).openDrawer(); + }, + ); + }, + ), + ), + Positioned( + top: 10, + right: 20, + child: FloatingActionButton( + onPressed: () async { + try { + Position position = await _determinePosition(); + + if (mounted) { + setState(() { + _currentP = LatLng( + position.latitude, + position.longitude, + ); + }); + + _animateCameraToPosition(_currentP!); + } + } catch (e) { + WarningSnackbar.show( + title: 'Ubicación desactivada', + message: + 'Por favor activa la ubicacion de tu telefono.', + ); + } + }, + elevation: 0, + child: const Icon( + Icons.gps_fixed, + size: 30, + ), + ), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10), + color: Colors.white, + child: Form( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + controller: _serviceTypeController, + readOnly: true, + onTap: () async { + final String? serviceType = await Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return const ServiceTypeScreen(); + }, + ), + ) as String?; + + if (serviceType != null) { + if (mounted) { + setState(() { + _serviceType = serviceType; + _serviceTypeController.text = _serviceType; + + updateMarkersForServiceType(_serviceType); + }); + } + } + }, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.room_service_rounded), + suffixIcon: Icon(Icons.arrow_drop_down), + hintText: 'Tipo de Servicio', + ), + ), + const SizedBox(height: 15), + TextFormField( + controller: _profesionalController, + readOnly: true, + onTap: () async { + final dynamic datos = await Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return ProfessionalScreen( + profession: _serviceTypeController.text, + ); + }, + ), + ); + if (datos != null) { + Professional? profesional = datos[0]; + ubicacion = datos[1]; + + if (profesional != null) { + if (mounted) { + setState(() { + _profesionalController.text = + profesional.name.toString(); + + professionalId = profesional.id; + + if (profesional.token != '') { + professionalToken = profesional.token!; + } + + professionalTarifa = profesional.tarifa ?? 0; + + professionalUbicacion = profesional.ubicacion; + + professionalLatitude = profesional.latitude; + professionalLongitude = profesional.longitude; + + if (ubicacion == 'sitio') { + professionalAddress = profesional.realAddress; + + _locacionController.text = + profesional.realAddress; + + if (professionalLatitude != null && + professionalLatitude != 0 && + professionalLongitude != null && + professionalLongitude != 0) { + _animateCameraToPosition(LatLng( + professionalLatitude!, + professionalLongitude!, + )); + } + } + }); + } + } + } + }, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.assignment_ind_rounded), + suffixIcon: Icon(Icons.arrow_drop_down), + hintText: 'Seleccionar profesional', + ), + ), + const SizedBox(height: 15), + TextFormField( + controller: _locacionController, + readOnly: ubicacion == 'sitio' ? true : false, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.near_me), + hintText: 'Dirección', + ), + onChanged: (value) { + String modifiedValue = value.replaceAll(' ', '_'); + placeAutoComplete(modifiedValue); + }, + ), + ubicacion == 'sitio' + ? const SizedBox() + : SizedBox( + height: _placesList.isNotEmpty ? 200 : 0, + child: ListView.builder( + itemCount: _placesList.length, + itemBuilder: (context, index) { + return ListTile( + onTap: () { + final selectedAddress = + _placesList[index]['formatted_address']; + final selectedLatitude = _placesList[index] + ['geometry']['location']['lat']; + final selectedLongitude = _placesList[index] + ['geometry']['location']['lng']; + + _locacionController.text = selectedAddress; + + _animateCameraToPosition(LatLng( + selectedLatitude, + selectedLongitude, + )); + + if (mounted) { + setState(() { + _placesList = []; + }); + } + }, + title: Text( + _placesList[index]['formatted_address']), + ); + }, + ), + ), + const SizedBox(height: 15), + Row( + children: [ + Expanded( + child: TextFormField( + readOnly: true, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.calendar_month), + suffixIcon: _selectedDate == null + ? const Icon(Icons.arrow_drop_down) + : null, + hintText: 'Fecha', + ), + controller: TextEditingController( + text: _selectedDate == null + ? '' + : formatter.format(_selectedDate!), + ), + onTap: () { + if (_profesionalController.text.isNotEmpty) { + _selectDateAndTime(context); + } else { + WarningSnackbar.show( + title: 'Selecciona un profesional', + message: + 'Selecciona un profesional antes de elegir la fecha y la hora de la cita.', + backgroundColor: Colors.orange, + ); + } + }, + ), + ), + Expanded( + child: TextFormField( + onTap: () { + if (_profesionalController.text.isNotEmpty) { + _selectDateAndTime(context); + } else { + WarningSnackbar.show( + title: 'Selecciona un profesional', + message: + 'Selecciona un profesional antes de elegir la fecha y la hora de la cita.', + backgroundColor: Colors.orange); + } + }, + readOnly: true, + decoration: const InputDecoration( + suffixIcon: Icon(Icons.arrow_drop_down), + hintText: 'Hora', + ), + controller: TextEditingController( + text: _selectedTime == null + ? '' + : ' ${_selectedTime!.format(context)}', + ), + )) + ], + ), + const SizedBox(height: 15), + TextFormField( + controller: _observacionController, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.message_outlined), + hintText: 'Observaciones', + ), + ), + const SizedBox(height: 20), + Center( + child: PrimaryButton( + onPressed: () { + if (user?.name == null || + user?.name == '' || + user?.phoneNumber == null || + user?.phoneNumber == '') { + WarningSnackbar.show( + title: 'Completa tu perfil', + message: + 'Diligencia la información de tu perfil antes de solicitar un servicio.', + ); + + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return const ProfileScreen(); + }, + ), + ); + } else { + try { + final DateTime combinedDate = DateTime( + _selectedDate!.year, + _selectedDate!.month, + _selectedDate!.day, + _selectedTime!.hour, + _selectedTime!.minute, + ); + + DateTime time2 = combinedDate.add( + const Duration(hours: 2), + ); + + UserModel.getUser(uid.toString()).then((value) { + eventoService + .createEvent( + value.name, + _observacionController.text, + '$_selectedDate', + '$combinedDate', + '$time2', + professionalId, + ubicacion, + _locacionController.text, + ubicacion != 'sitio' + ? coordinates.latitude + : professionalLatitude, + ubicacion != 'sitio' + ? coordinates.longitude + : professionalLongitude, + 'pendiente', + professionalTarifa == 0 + ? 0 + : professionalTarifa, + false, + false, + ) + .then((value) { + if (professionalToken != '') { + sendPushNotification(professionalToken); + } + Navigator.pushReplacement( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return ServiceAfterScreen( + eventoId: value, + ); + }, + ), + ); + }); + }); + } catch (e) { + WarningSnackbar.show( + title: 'Llena todos los campos', + message: + 'Asegurate de llenar todos los campos.', + ); + } + } + }, + text: 'Solicitar servicio', + minWidth: 230, + ), + ), + ], + ), + ), + ) + ], + ), + ), + ); + } + + Future getLocationUpdates() async { + bool _serviceEnabled; + PermissionStatus _permissionGranted; + + _serviceEnabled = await _locationController.serviceEnabled(); + if (_serviceEnabled) { + _serviceEnabled = await _locationController.requestService(); + } else { + return; + } + + _permissionGranted = await _locationController.hasPermission(); + if (_permissionGranted == PermissionStatus.denied) { + _permissionGranted = await _locationController.requestPermission(); + if (_permissionGranted != PermissionStatus.granted) { + return; + } + } + + _locationController.onLocationChanged + .listen((LocationData currentLocation) { + if (currentLocation.latitude != null && + currentLocation.longitude != null) { + if (mounted) { + setState(() { + _currentP = LatLng( + currentLocation.latitude!, + currentLocation.longitude!, + ); + }); + } + } + }); + } + + Future> getPolylinePoints() async { + List polylineCoordinates = []; + PolylinePoints polylinePoints = PolylinePoints(); + PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( + GOOGLE_MAPS_API_KEY, + PointLatLng(_pGooglePlex.latitude, _pGooglePlex.longitude), + PointLatLng(_pApplePark.latitude, _pApplePark.longitude), + travelMode: TravelMode.driving, + ); + if (result.points.isNotEmpty) { + result.points.forEach((PointLatLng point) { + polylineCoordinates.add(LatLng(point.latitude, point.longitude)); + }); + } else { + print(result.errorMessage); + } + return polylineCoordinates; + } + + void generatePolyLineFromPoints(List polylineCoordinates) async { + PolylineId id = const PolylineId("poly"); + Polyline polyline = Polyline( + polylineId: id, + color: Colors.blue, + points: polylineCoordinates, + width: 6, + ); + if (mounted) { + setState(() { + polylines[id] = polyline; + }); + } + } + + void _applyMapStyle(GoogleMapController controller) { + controller.setMapStyle(MAP_STYLE); + } + + Future _animateCameraToPosition(LatLng position) async { + final GoogleMapController controller = await _mapController.future; + controller.animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition( + target: position, + zoom: 17.5, + ), + ), + ); + } + + Future _animateInitialCameraToPosition(String coordsOfCity) async { + List coords = coordsOfCity.split(','); + double lat = double.parse(coords[0]); + double lng = double.parse(coords[1]); + + _animateCameraToPosition(LatLng(lat, lng)); + + try { + Position position = await _determinePosition(); + + _animateCameraToPosition(LatLng(position.latitude, position.longitude)); + + if (mounted) { + setState(() {}); + } + } catch (e) { + print('Error: $e'); + } + } + + void updateMarkersForServiceType(String serviceType) { + getUsersWithActiveStatus(serviceType).then((value) { + markers.clear(); + for (var doc in value) { + final element = doc.data()!; + + if (element['latitude'] != null && element['longitude'] != null) { + markers.add( + Marker( + icon: _markerIcon!, + markerId: MarkerId(doc.id), + position: LatLng( + element['latitude'], + element['longitude'], + ), + ), + ); + } + } + }).catchError((e) { + print('Error al actualizar los marcadores: $e'); + }); + } + + Future _determinePosition() async { + bool serviceEnabled; + LocationPermission permission; + + serviceEnabled = await Geolocator.isLocationServiceEnabled(); + + if (!serviceEnabled) { + return Future.error('Location services are disabled'); + } + + permission = await Geolocator.checkPermission(); + + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + + if (permission == LocationPermission.denied) { + return Future.error('Location permission denied'); + } + } + + if (permission == LocationPermission.deniedForever) { + return Future.error('Location permissions are permanently denied'); + } + + Position position = await Geolocator.getCurrentPosition(); + + return position; + } + + Future getLocationName(double latitude, double longitude) async { + String address; + try { + List placemarks = + await geocoding.placemarkFromCoordinates(latitude, longitude); + geocoding.Placemark place = placemarks[0]; + + if (place.thoroughfare != '' || place.subThoroughfare != '') { + address = + "${place.thoroughfare} ${place.subThoroughfare} ${place.subLocality}, ${place.locality}, ${place.administrativeArea}"; + } else { + address = ''; + } + } catch (e) { + address = ''; + } + + return address; + } + + void placeAutoComplete(String query) async { + Uri uri = Uri.https("admin.prosapp.co", "/autocomplete", { + "input": query, + "location": _coordsOfCity, + }); + + String? response = await NetworkUtility.fetchUrl(uri); + + if (response != null) { + if (mounted) { + setState(() { + _placesList = jsonDecode(response.toString())['results']; + }); + } + } + } + + Future _selectDate(BuildContext context) async { + final DateTime? picked = await showDatePicker( + context: context, + initialDate: now, + firstDate: now, + lastDate: DateTime(now.year + 1), + ); + + if (picked != null && picked != _selectedDate) { + if (mounted) { + setState(() { + _selectedDate = picked; + }); + } + } + } + + Future _selectTime(BuildContext context) async { + final TimeOfDay? pickedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ); + + if (pickedTime != null) { + if (mounted) { + setState(() { + _selectedTime = pickedTime; + }); + } + } + } + + Future>>> getUsersWithActiveStatus( + String serviceType) async { + dynamic querySnapshot; + + if (serviceType != "Servicio") { + querySnapshot = await FirebaseFirestore.instance + .collection('users') + .where('estado', isEqualTo: 'activo') + .where('profesion', isEqualTo: serviceType) + .get(); + } else { + querySnapshot = await FirebaseFirestore.instance + .collection('users') + .where('estado', isEqualTo: 'activo') + .get(); + } + + return querySnapshot.docs; + } + + void _saveToken() async { + FirebaseMessaging messaging = FirebaseMessaging.instance; + + final token = await messaging.getToken(); + + try { + await FirebaseFirestore.instance + .collection('users') + .doc(uid) + .update({'token': token}); + } catch (e) { + print(e); + } + } + + Future _getAppVersion() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + String version = packageInfo.version; + + if (mounted) { + setState(() { + appVersion = version; + }); + } + + if (settings != null) { + _checkForUpdate(settings); + } + } + + void _checkForUpdate(String? settings) { + if (appVersion == settings) { + showDialog( + context: context, + builder: (context) { + return WillPopScope( + onWillPop: () async { + return false; + }, + child: Center( + child: AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + title: const Text( + "Debes Actualizar la Aplicación", + style: TextStyle(fontWeight: FontWeight.bold), + ), + content: const Text( + "Tienes una versión desactualizada de nuestra aplicación. Te recomendamos actualizarla para disfrutar de las últimas características y mejoras.", + ), + actions: [ + TextButton( + child: const Text( + "Actualizar", + style: + TextStyle(fontWeight: FontWeight.w600, fontSize: 20), + ), + onPressed: () { + launchUrl(_url); + }, + ), + ], + ), + ), + ); + }, + ); + } + } + + Future _selectDateAndTime(BuildContext context) async { + final DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: now, + firstDate: now, + lastDate: DateTime(now.year + 1), + ); + + if (pickedDate == null) { + return; + } + + final TimeOfDay? pickedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ); + + if (pickedTime != null) { + final DateTime selectedDateTime = DateTime( + pickedDate.year, + pickedDate.month, + pickedDate.day, + pickedTime.hour, + pickedTime.minute, + ); + + if (selectedDateTime.isBefore(DateTime.now())) { + WarningSnackbar.show( + title: 'Hora inválida', + message: + 'La hora seleccionada no es valida porque es anterior a la hora actual.', + ); + return; + } + + if (mounted) { + setState(() { + _selectedDate = pickedDate; + _selectedTime = pickedTime; + }); + } + } + } + + Future sendPushNotification(String pro) async { + try { + http.Response response = await http.post( + Uri.parse('https://fcm.googleapis.com/fcm/send'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': + 'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2', + }, + body: jsonEncode( + { + 'notification': { + 'body': 'alguien a solicitado tus servicios', + 'title': 'Nueva solicitud', + }, + 'priority': 'high', + 'data': { + 'click_action': 'FLUTTER_NOTIFICATION_CLICK', + 'id': '1', + 'status': 'done', + 'screen': 'solicitud' + }, + 'to': pro + }, + ), + ); + + response; + } catch (e) { + print('error al enviar notificacion $e'); + } + } +} diff --git a/lib/src/presentation/screens/professional_profile.dart b/lib/src/presentation/screens/professional_profile.dart index d7b487f..4846d09 100644 --- a/lib/src/presentation/screens/professional_profile.dart +++ b/lib/src/presentation/screens/professional_profile.dart @@ -8,6 +8,7 @@ import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/controllers/info_%20professional.dart'; +import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; import 'package:prosappco/src/services/select_image_profile.dart'; import 'package:file_picker/file_picker.dart'; @@ -187,7 +188,7 @@ class ProfessionalProfileScreenState extends State { Reference ref = storage.ref().child('users').child(uid!).child('cedula').child(random); - final UploadTask uploadTask = ref.putFile(image); + final UploadTask uploadTask = ref.putFile(image, metadata); final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); @@ -217,6 +218,10 @@ class ProfessionalProfileScreenState extends State { } } + final metadata = SettableMetadata( + contentType: 'application/pdf', + ); + Future uploadCertificado(File image) async { final now = DateTime.now(); final formattedDate = DateFormat('HHmmssddMMyyyy').format(now); @@ -230,7 +235,7 @@ class ProfessionalProfileScreenState extends State { .child('certificado_profesional') .child(random); - final UploadTask uploadTask = ref.putFile(image); + final UploadTask uploadTask = ref.putFile(image, metadata); final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); @@ -281,24 +286,31 @@ class ProfessionalProfileScreenState extends State { } Future uploadImage(File image) async { - final String namefile = image.path.split('/').last; + try { + final String namefile = image.path.split('/').last; - Reference ref = storage - .ref() - .child('users') - .child(uid!) - .child('profile') - .child(namefile); + Reference ref = storage + .ref() + .child('users') + .child(uid!) + .child('profile') + .child(namefile); - final UploadTask uploadTask = ref.putFile(image); + final UploadTask uploadTask = ref.putFile(image); - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); + final TaskSnapshot snapshot = await uploadTask; - photoTemp = ref.fullPath; + if (snapshot.state == TaskState.success) { + // Obtén la URL de descarga de la imagen y actualiza en Firestore + String downloadURL = await ref.getDownloadURL(); + await updateImage(downloadURL); - if (snapshot.state == TaskState.success) { - return true; - } else { + return true; + } else { + return false; + } + } catch (e) { + print('Error al cargar la imagen: $e'); return false; } } @@ -361,7 +373,7 @@ class ProfessionalProfileScreenState extends State { .child('especializaciones') .child('e${DateTime.now().millisecondsSinceEpoch}.pdf'); - final UploadTask uploadTask = ref.putFile(image); + final UploadTask uploadTask = ref.putFile(image, metadata); final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); @@ -419,22 +431,47 @@ class ProfessionalProfileScreenState extends State { especializacion.split(',').map((e) => e.trim()).toList(); if (cedula.isEmpty) { - _showCustomSnackBar( - context, 'Por favor, adjunta el documento PDF de tu cédula.'); + WarningSnackbar.show( + title: 'Te faltan campos!!', + message: 'Por favor, ingresa tu cedula', + ); return; } if (image_cedula == null) { - _showCustomSnackBar( - context, 'Por favor, adjunte el documento PDF de su cédula.'); + WarningSnackbar.show( + title: 'Te faltan archivos!!', + message: 'Por favor, adjunta el documento PDF de tu cedula', + ); + return; + } + + if (_profession.isEmpty) { + WarningSnackbar.show( + title: 'Te falta elegir una profesión!!', + message: + 'Por favor, elige tu profesión antes de enviar la información.', + ); return; } if (image_certificado == null) { - _showCustomSnackBar(context, - 'Por favor, adjunta el documento PDF de tu certificado. ¡Gracias por tu colaboración!'); + WarningSnackbar.show( + title: 'Te faltan archivos!!', + message: 'Por favor, adjunta el documento PDF de tu certificado', + ); return; } + if (imagen_to_upload == null) { + WarningSnackbar.show( + title: 'Sube una foto de perfil', + message: 'Para continuar debes subir una imagen de perfil', + ); + return; + } else { + // Actualiza la imagen de perfil si hay cambios + updateImage(photoTemp); + } // Actualiza los datos del usuario en Firestore await FirebaseFirestore.instance.collection('users').doc(uid).update({ @@ -448,11 +485,6 @@ class ProfessionalProfileScreenState extends State { uploadCertificado(image_certificado!); uploadEspecializaciones(images_especializacion); - // Actualiza la imagen de perfil si hay cambios - if (imagen_to_upload != null) { - updateImage(photoTemp); - } - // Navega a la siguiente pantalla Navigator.pushReplacementNamed(context, '/solicitudEnviada'); } diff --git a/lib/src/presentation/screens/profile/profile.dart b/lib/src/presentation/screens/profile/profile.dart index 0c9098c..4d0cec8 100644 --- a/lib/src/presentation/screens/profile/profile.dart +++ b/lib/src/presentation/screens/profile/profile.dart @@ -13,9 +13,13 @@ import 'package:prosappco/src/presentation/widgets/profile/birth_date_picker.dar import 'package:prosappco/src/presentation/screens/city.dart'; import 'package:prosappco/src/presentation/screens/new_number.dart'; import 'package:prosappco/src/presentation/screens/new_password.dart'; +import 'package:prosappco/src/presentation/widgets/shared/primary_checkbox.dart'; +import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; +import 'package:prosappco/src/providers/user_provider.dart'; import 'package:prosappco/src/services/select_image_profile.dart'; import 'package:prosappco/src/presentation/widgets/profile/gender_dropdown.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; +import 'package:provider/provider.dart'; import '../../../components/photo_view.dart'; class ProfileScreen extends StatefulWidget { @@ -275,13 +279,15 @@ class _ProfileScreenState extends State { return; } - if (newEmail.isEmpty) { - Get.snackbar( - 'Correo Invalido', - 'Ingresa un email válido.', - snackPosition: SnackPosition.BOTTOM, - ); - return; + if (enableLoginWithEmail) { + if (newEmail.isEmpty) { + Get.snackbar( + 'Correo Invalido', + 'Ingresa un email válido.', + snackPosition: SnackPosition.BOTTOM, + ); + return; + } } if (currentUser?.displayName != newName) { @@ -296,18 +302,29 @@ class _ProfileScreenState extends State { } } - if (currentUser?.email != newEmail) { - if (newPassword.isNotEmpty) { - await FirebaseFirestore.instance.collection('users').doc(uid).update({ - 'email': newEmail, - }); - updateEmailAndPassword(newEmail, newPassword); - } else { - Get.snackbar( - 'Contraseña Invalida', - 'Porfavor ingresa una contraseña.', - snackPosition: SnackPosition.BOTTOM, - ); + if (enableLoginWithEmail) { + if (currentUser?.email != newEmail) { + if (newPassword.isNotEmpty) { + bool updateEmailSuccess = + await updateEmailAndPassword(newEmail, newPassword); + + if (updateEmailSuccess) { + await FirebaseFirestore.instance + .collection('users') + .doc(uid) + .update({ + 'email': newEmail, + }); + } else { + return; + } + } else { + Get.snackbar( + 'Contraseña Invalida', + 'Por favor ingresa una contraseña.', + snackPosition: SnackPosition.BOTTOM, + ); + } } } @@ -320,18 +337,8 @@ class _ProfileScreenState extends State { print('e'); } - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'email': newEmail}); - } catch (e) { - print('Error al actualizar la imagen de perfil $e'); - } - try { if (imagen_to_upload == null) { - return; } else { final uploaded = await uploadImage(imagen_to_upload!); updateImage(photoTemp); @@ -341,10 +348,14 @@ class _ProfileScreenState extends State { print('Error al actualizar la imagen de perfil $e'); } - Get.snackbar( - 'Informacion actualizada', - 'Tu informacion ha sido actualizada con exito.', - snackPosition: SnackPosition.BOTTOM, + WarningSnackbar.show( + title: 'Informacion actualizada', + message: 'Tu informacion ha sido actualizada con exito.', + icon: const Icon( + Icons.check, + color: Colors.white, + ), + backgroundColor: Colors.green, ); } @@ -401,19 +412,23 @@ class _ProfileScreenState extends State { _email = newEmail; }); - Get.snackbar( - 'Éxito', - 'Correo electrónico actualizado correctamente.', - snackPosition: SnackPosition.BOTTOM, + WarningSnackbar.show( + title: 'Actualizado exitosamente', + message: 'Correo electronico actualizado correctamente.', + icon: const Icon(Icons.check, color: Colors.white), + backgroundColor: Colors.green, ); - Navigator.of(context).pop(); + if (Navigator.canPop(context)) { + Navigator.of(context).pop(); + } } catch (e) { - Get.snackbar( - 'No se pudo actualizar el correo', - 'Verifica tu contraseña actual y asegúrate de que el nuevo correo electrónico no se haya utilizado previamente.', - snackPosition: SnackPosition.BOTTOM, + WarningSnackbar.show( + title: 'No se pudo actualizar el correo', + message: + 'Verifica tu contraseña actual y asegúrate de que el nuevo correo electrónico no se haya utilizado previamente.', ); + print('Error al actualizar el correo electrónico: $e'); } } @@ -472,23 +487,26 @@ class _ProfileScreenState extends State { ); } - Future updateEmailAndPassword(String email, String password) async { + Future updateEmailAndPassword(String email, String password) async { final User? user = FirebaseAuth.instance.currentUser; if (user != null) { try { await user.updateEmail(email); await user.updatePassword(password); + + return true; } catch (e) { - Get.snackbar( - 'Agregar correo', - 'Inicia sesión para asegurarnos de que seas tú.', - snackPosition: SnackPosition.BOTTOM, - ); - // AuthenticationRepository.instance.logout(uid!); + WarningSnackbar.show( + title: 'Inicia sesión de nuevo', + message: + 'Para agregar un correo debes haber iniciado sesión recientemente.'); } } + return false; } + bool enableLoginWithEmail = false; + Future _showChoiceDialog(BuildContext context) async { return showDialog( context: context, @@ -538,6 +556,8 @@ class _ProfileScreenState extends State { Widget build(BuildContext context) { String city = _ciudad.toString(); + final userProvider = Provider.of(context); + return Scaffold( appBar: PopAppbar( onPressed: () { @@ -582,70 +602,7 @@ class _ProfileScreenState extends State { prefixIcon: Icon(Icons.person_outline), hintText: 'Nombre (Obligatorio)'), ), - const SizedBox(height: 0), - _email != null - ? TextFormField( - onTap: () { - _showEmailUpdateDialog(context); - }, - readOnly: true, - controller: _emailController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.email_outlined), - hintText: 'Email (Obligatorio)'), - ) - : TextFormField( - controller: _emailController, - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Por favor ingrese un email'; - } - final RegExp emailRegExp = - RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - if (!emailRegExp.hasMatch(value)) { - return 'Por favor ingrese un email válido'; - } - return null; - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.email_outlined), - hintText: 'Email (Obligatorio)'), - ), - const SizedBox(height: 20.0), - _email != null - ? const SizedBox.shrink() - : TextFormField( - controller: _passwordController, - obscureText: _obscureText, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Porfavor ingrese una contraseña.'; - } - if (value.length < 5) { - return 'Debe tener al menos 5 caracteres.'; - } - return null; - }, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, - ), - hintText: 'Contraseña (Obligatorio)'), - ), - _email != null - ? const SizedBox.shrink() - : const SizedBox(height: 20.0), + const SizedBox(), TextFormField( readOnly: true, onTap: () async { @@ -719,6 +676,111 @@ class _ProfileScreenState extends State { ), ) : const SizedBox(), + _email != null + ? TextFormField( + onTap: () { + _showEmailUpdateDialog(context); + }, + readOnly: true, + controller: _emailController, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.email_outlined), + hintText: 'Email (Obligatorio)'), + ) + : const SizedBox(), + const SizedBox(height: 20), + _email == null + ? PrimaryCheckbox( + text: + 'Habilitar inicio de sesión con correo (Opcional)', + initialValue: enableLoginWithEmail, + onChanged: (value) { + setState(() { + enableLoginWithEmail = value; + }); + }, + ) + : const SizedBox(), + const SizedBox(height: 15), + enableLoginWithEmail + ? Container( + decoration: BoxDecoration( + border: Border.all( + color: Colors.blue, + width: 0.5, + ), + borderRadius: BorderRadius.circular(10), + ), + padding: const EdgeInsets.all(10), + child: Column( + children: [ + TextFormField( + controller: _emailController, + validator: (String? value) { + if (enableLoginWithEmail) { + if (value == null || value.isEmpty) { + return 'Por favor ingrese un email'; + } + final RegExp emailRegExp = RegExp( + r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); + if (!emailRegExp.hasMatch(value)) { + return 'Por favor ingrese un email válido'; + } + return null; + } else { + return null; + } + }, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.email_outlined), + hintText: 'Email'), + ), + const SizedBox(height: 20.0), + _email != null + ? const SizedBox.shrink() + : TextFormField( + controller: _passwordController, + obscureText: _obscureText, + validator: (value) { + if (enableLoginWithEmail) { + if (value == null || + value.isEmpty) { + return 'Por favor ingrese una contraseña.'; + } + if (value.length < 5) { + return 'Debe tener al menos 5 caracteres.'; + } + return null; + } else { + return null; + } + }, + decoration: InputDecoration( + prefixIcon: const Icon( + Icons.lock_outline), + suffixIcon: IconButton( + icon: Icon( + _obscureText + ? Icons.visibility + : Icons.visibility_off, + color: Colors.grey, + ), + onPressed: () { + setState(() { + _obscureText = + !_obscureText; + }); + }, + ), + hintText: 'Contraseña'), + ), + _email != null + ? const SizedBox.shrink() + : const SizedBox(height: 20), + ], + ), + ) + : const SizedBox(), ], ), ), @@ -752,6 +814,8 @@ class _ProfileScreenState extends State { if (_formKey.currentState!.validate()) { await updateInfo(); } + + await userProvider.updateUserDataAndScores(); }, text: 'Guardar', ), diff --git a/lib/src/presentation/screens/service_after.dart b/lib/src/presentation/screens/service_after.dart index 7ae0a97..a69b6eb 100644 --- a/lib/src/presentation/screens/service_after.dart +++ b/lib/src/presentation/screens/service_after.dart @@ -8,7 +8,7 @@ import 'package:prosappco/src/models/event_model.dart'; import 'package:prosappco/src/models/scores_model.dart'; import 'package:prosappco/src/models/setting_model.dart'; import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/screens/service.dart'; +import 'package:prosappco/src/presentation/screens/map/service.dart'; class ServiceAfterScreen extends StatefulWidget { var eventoId; diff --git a/lib/src/presentation/screens/service.dart b/lib/src/presentation/screens/service_old.dart similarity index 98% rename from lib/src/presentation/screens/service.dart rename to lib/src/presentation/screens/service_old.dart index f6ccd1a..92a3a9c 100644 --- a/lib/src/presentation/screens/service.dart +++ b/lib/src/presentation/screens/service_old.dart @@ -30,14 +30,14 @@ import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; import 'package:url_launcher/url_launcher.dart'; -class ServiceScreen extends StatefulWidget { - const ServiceScreen({super.key}); +class ServiceOldScreen extends StatefulWidget { + const ServiceOldScreen({super.key}); @override - State createState() => _ServiceScreenState(); + State createState() => _ServiceOldScreenState(); } -class _ServiceScreenState extends State { +class _ServiceOldScreenState extends State { late String appVersion; final Uri _url = Uri.parse( 'https://play.google.com/store/apps/details?id=com.prosapp.prosapp'); @@ -57,27 +57,6 @@ class _ServiceScreenState extends State { } } - // Future> getRouteCoordinates( - // double startLat, double startLng, double endLat, double endLng) async { - // List polylineCoordinates = []; - - // PolylinePoints polylinePoints = PolylinePoints(); - - // PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( - // 'AIzaSyCW_og6qQ8W8G-5_BxIS4sBnl8cLkjL95s', - // PointLatLng(startLat, startLng), - // PointLatLng(endLat, endLng), - // ); - - // if (result.points.isNotEmpty) { - // result.points.forEach((PointLatLng point) { - // polylineCoordinates.add(LatLng(point.latitude, point.longitude)); - // }); - // } - - // return polylineCoordinates; - // } - Future sendPushNotification(String pro) async { try { http.Response response = await http.post( @@ -1217,9 +1196,10 @@ class _ServiceScreenState extends State { const SizedBox(height: 15), TextFormField( controller: _observacionController, - decoration: const InputDecoration( + decoration: InputDecoration( prefixIcon: Icon(Icons.message_outlined), - hintText: 'Observaciones'), + hintText: + 'Observaciones ${user?.name} - ${user?.phoneNumber}'), ), const SizedBox(height: 20), PrimaryButton( diff --git a/lib/src/presentation/widgets/shared/drawer_menu.dart b/lib/src/presentation/widgets/shared/drawer_menu.dart index 3e9e41f..bc8cb5c 100644 --- a/lib/src/presentation/widgets/shared/drawer_menu.dart +++ b/lib/src/presentation/widgets/shared/drawer_menu.dart @@ -6,6 +6,7 @@ import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/models/scores_model.dart'; import 'package:prosappco/src/models/user_model.dart'; +import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; import 'package:prosappco/src/providers/user_provider.dart'; import 'package:prosappco/src/presentation/screens/configuracion.dart'; import 'package:prosappco/src/presentation/screens/messages_user.dart'; @@ -322,10 +323,10 @@ class DrawerMenu extends StatelessWidget { user?.city == '' || user?.phoneNumber == '' || user?.phoneNumber == null) { - Get.snackbar( - 'Completa tu perfil', - 'Para ser un profesional registrado, asegúrate de llenar todos los campos necesarios y no olvides guardar tus cambios para que surtan efecto.', - snackPosition: SnackPosition.BOTTOM, + WarningSnackbar.show( + title: 'Completa tu perfil', + message: + 'Para ser un profesional registrado, asegúrate de llenar todos los campos necesarios y no olvides guardar tus cambios para que surtan efecto.', ); } else { if (user?.phoneNumber != user?.phoneNumber) { diff --git a/lib/src/providers/user_provider.dart b/lib/src/providers/user_provider.dart index 19afc58..f884178 100644 --- a/lib/src/providers/user_provider.dart +++ b/lib/src/providers/user_provider.dart @@ -35,6 +35,20 @@ class UserProvider extends ChangeNotifier { notifyListeners(); } + Future updateUserDataAndScores() async { + var uid = FirebaseAuth.instance.currentUser?.uid; + if (uid == null) return; + + var documentSnapshot = await _firestore.collection('users').doc(uid).get(); + + if (documentSnapshot.exists) { + final data = documentSnapshot.data() as Map; + _user = UserModel.fromFirestore(data); + _score = await ScoresModel.scoreTo(uid, false, false); + notifyListeners(); + } + } + UserModel? get user => _user; ScoresModel? get score => _score; } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 2758605..7ebb5b5 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,6 +13,7 @@ import firebase_messaging import firebase_storage import flutter_local_notifications import geolocator_apple +import location import package_info_plus import shared_preferences_foundation import url_launcher_macos @@ -26,6 +27,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) + LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 49bed58..e668bb8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -773,6 +773,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + location: + dependency: "direct main" + description: + name: location + sha256: "06be54f682c9073cbfec3899eb9bc8ed90faa0e17735c9d9fa7fe426f5be1dd1" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + location_platform_interface: + dependency: transitive + description: + name: location_platform_interface + sha256: "8aa1d34eeecc979d7c9fe372931d84f6d2ebbd52226a54fe1620de6fdc0753b1" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + location_web: + dependency: transitive + description: + name: location_web + sha256: ec484c66e8a4ff1ee5d044c203f4b6b71e3a0556a97b739a5bc9616de672412b + url: "https://pub.dev" + source: hosted + version: "4.2.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a825cde..478a3ff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,9 @@ dependencies: sdk: flutter shared_preferences: ^2.0.10 cupertino_icons: ^1.0.2 + location: ^5.0.3 + flutter_polyline_points: ^2.0.0 + google_maps_flutter: ^2.5.0 firebase_auth: ^4.6.2 firebase_core: ^2.13.1 file_picker: ^5.3.2 @@ -51,8 +54,6 @@ dependencies: flutter_animate: flutter_rating_bar: table_calendar: - google_maps_flutter: ^2.5.0 - flutter_polyline_points: ^2.0.0 http: ^1.1.0 geolocator: ^9.0.2 geocoding: ^2.1.0