notificaciones al pedir servicio y al enviar mensaje de chat
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
<meta-data android:name="com.google.android.geo.API_KEY"
|
||||
android:value="AIzaSyCW_og6qQ8W8G-5_BxIS4sBnl8cLkjL95s"/>
|
||||
<activity
|
||||
android:showWhenLocked="true"
|
||||
android:turnScreenOn="true"
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 348 KiB |
Binary file not shown.
+6
-1
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:injector/injector.dart';
|
||||
import 'package:prosappco/blocs/authentication_bloc/authentication_bloc.dart';
|
||||
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
|
||||
import 'package:prosappco/blocs/notification_bloc/notification_bloc.dart';
|
||||
import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart';
|
||||
import 'package:prosappco/blocs/professional_profile_bloc/professional_profile_bloc.dart';
|
||||
import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart';
|
||||
@@ -29,7 +30,11 @@ class MainApp extends StatelessWidget {
|
||||
create: (context) => Injector.appInstance.get<ProfessionalBloc>(),
|
||||
),
|
||||
BlocProvider<ProfessionalProfileBloc>(
|
||||
create: (context) => Injector.appInstance.get<ProfessionalProfileBloc>(),
|
||||
create: (context) =>
|
||||
Injector.appInstance.get<ProfessionalProfileBloc>(),
|
||||
),
|
||||
BlocProvider<NotificationBloc>(
|
||||
create: (context) => Injector.appInstance.get<NotificationBloc>(),
|
||||
)
|
||||
],
|
||||
child: BlocBuilder<MyUserBloc, MyUserState>(
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:prosappco/firebase_options.dart';
|
||||
import 'package:prosappco/local_notifications/local_notifications.dart';
|
||||
import 'package:user_repository/user_repository.dart';
|
||||
|
||||
part 'notification_event.dart';
|
||||
part 'notification_state.dart';
|
||||
|
||||
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
await Firebase.initializeApp();
|
||||
}
|
||||
|
||||
class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
|
||||
FirebaseMessaging messaging = FirebaseMessaging.instance;
|
||||
|
||||
final Future<void> Function() requestLocalNotificationPermission;
|
||||
final void Function({
|
||||
required int id,
|
||||
String? title,
|
||||
String? body,
|
||||
String? data,
|
||||
}) showLocalNotification;
|
||||
|
||||
NotificationBloc(
|
||||
{required this.requestLocalNotificationPermission,
|
||||
required this.showLocalNotification})
|
||||
: super(const NotificationState()) {
|
||||
on<NotificationStatusChanged>(_notificationStatusChanged);
|
||||
|
||||
_initialStatusCheck();
|
||||
|
||||
_onForegroundMessage();
|
||||
}
|
||||
|
||||
static Future<void> initializeFCM() async {
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
}
|
||||
|
||||
void _notificationStatusChanged(
|
||||
NotificationStatusChanged event, Emitter<NotificationState> emit) {
|
||||
emit(state.copyWith(status: event.status));
|
||||
|
||||
_getFCMToken();
|
||||
}
|
||||
|
||||
void _initialStatusCheck() async {
|
||||
final settings = await messaging.getNotificationSettings();
|
||||
add(NotificationStatusChanged(settings.authorizationStatus));
|
||||
_getFCMToken();
|
||||
}
|
||||
|
||||
Future<void> _saveFCMTokenToFirestore(String token) async {
|
||||
try {
|
||||
CollectionReference users = FirebaseFirestore.instance.collection('users');
|
||||
|
||||
await users.doc(FirebaseAuth.instance.currentUser!.uid).update({
|
||||
'token': token,
|
||||
});
|
||||
} catch (e) {
|
||||
print('tokenFCM Error saving FCM token to Firestore: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _getFCMToken() async {
|
||||
if (state.status != AuthorizationStatus.authorized) return;
|
||||
|
||||
final fcmToken = await messaging.getToken();
|
||||
log('tokenFCM ${fcmToken.toString()}');
|
||||
|
||||
if (fcmToken == null) return;
|
||||
|
||||
_saveFCMTokenToFirestore(fcmToken);
|
||||
}
|
||||
|
||||
void _handleRemoteMessage(RemoteMessage message) {
|
||||
if (message.notification == null) return;
|
||||
|
||||
showLocalNotification(
|
||||
id: 1,
|
||||
title: message.notification!.title,
|
||||
body: message.notification!.body,
|
||||
);
|
||||
}
|
||||
|
||||
void _onForegroundMessage() {
|
||||
FirebaseMessaging.onMessage.listen(_handleRemoteMessage);
|
||||
}
|
||||
|
||||
void requestPermission() async {
|
||||
NotificationSettings settings = await messaging.requestPermission(
|
||||
alert: true,
|
||||
announcement: false,
|
||||
badge: true,
|
||||
carPlay: false,
|
||||
criticalAlert: false,
|
||||
provisional: false,
|
||||
sound: true,
|
||||
);
|
||||
|
||||
// Solicitar permiso a las local notificaciones
|
||||
|
||||
await requestLocalNotificationPermission();
|
||||
|
||||
add(NotificationStatusChanged(settings.authorizationStatus));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
part of 'notification_bloc.dart';
|
||||
|
||||
abstract class NotificationEvent extends Equatable {
|
||||
const NotificationEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class NotificationStatusChanged extends NotificationEvent {
|
||||
final AuthorizationStatus status;
|
||||
|
||||
const NotificationStatusChanged(this.status);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
part of 'notification_bloc.dart';
|
||||
|
||||
class NotificationState extends Equatable {
|
||||
final AuthorizationStatus status;
|
||||
|
||||
const NotificationState({
|
||||
this.status = AuthorizationStatus.notDetermined,
|
||||
});
|
||||
|
||||
NotificationState copyWith({
|
||||
AuthorizationStatus? status,
|
||||
}) =>
|
||||
NotificationState(
|
||||
status: status ?? this.status,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object> get props => [status];
|
||||
}
|
||||
|
||||
class NotificationInitial extends NotificationState {}
|
||||
@@ -7,6 +7,7 @@ import 'package:prosappco/blocs/auth_bloc/auth_bloc.dart';
|
||||
import 'package:prosappco/blocs/authentication_bloc/authentication_bloc.dart';
|
||||
import 'package:prosappco/blocs/chat_bloc/chat_bloc.dart';
|
||||
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
|
||||
import 'package:prosappco/blocs/notification_bloc/notification_bloc.dart';
|
||||
import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart';
|
||||
import 'package:prosappco/blocs/professional_list_bloc/professional_list_bloc.dart';
|
||||
import 'package:prosappco/blocs/professional_profile_bloc/professional_profile_bloc.dart';
|
||||
@@ -16,6 +17,7 @@ import 'package:prosappco/blocs/service_bloc/service_bloc.dart';
|
||||
import 'package:prosappco/blocs/setting_bloc/setting_bloc.dart';
|
||||
import 'package:prosappco/blocs/sign_up_bloc/sign_up_bloc.dart';
|
||||
import 'package:prosappco/blocs/sing_in_bloc/sign_in_bloc.dart';
|
||||
import 'package:prosappco/local_notifications/local_notifications.dart';
|
||||
import 'package:score_repository/score_repository.dart';
|
||||
import 'package:service_repository/service_repository.dart';
|
||||
import 'package:user_repository/user_repository.dart';
|
||||
@@ -56,6 +58,12 @@ class AppDI {
|
||||
professionalRepository: injector.get(),
|
||||
userRepository: injector.get<UserRepository>()),
|
||||
);
|
||||
injector.registerSingleton(
|
||||
() => NotificationBloc(
|
||||
requestLocalNotificationPermission: LocalNotifications.requestPermissionLocalNotifications,
|
||||
showLocalNotification: LocalNotifications.showLocalNotification,
|
||||
),
|
||||
);
|
||||
|
||||
injector.registerDependency<SignInBloc>(
|
||||
() => SignInBloc(userRepository: injector.get<UserRepository>()));
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
class LocalNotifications {
|
||||
static Future<void> requestPermissionLocalNotifications() async {
|
||||
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
|
||||
await flutterLocalNotificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermission();
|
||||
}
|
||||
|
||||
static Future<void> initializeLocalNotifications() async {
|
||||
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
|
||||
const initializationSettingsAndroid =
|
||||
AndroidInitializationSettings('app_icon');
|
||||
|
||||
const initializationSettings = InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
);
|
||||
|
||||
await flutterLocalNotificationsPlugin.initialize(
|
||||
initializationSettings,
|
||||
);
|
||||
}
|
||||
|
||||
static void showLocalNotification({
|
||||
required int id,
|
||||
String? title,
|
||||
String? body,
|
||||
String? data,
|
||||
}) {
|
||||
const androidDetails = AndroidNotificationDetails(
|
||||
'channel_id',
|
||||
'channel_name',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
playSound: true,
|
||||
sound: RawResourceAndroidNotificationSound('notification'),
|
||||
);
|
||||
|
||||
const notificationDetails = NotificationDetails(
|
||||
android: androidDetails,
|
||||
);
|
||||
|
||||
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
|
||||
flutterLocalNotificationsPlugin.show(
|
||||
id,
|
||||
title,
|
||||
body,
|
||||
notificationDetails,
|
||||
payload: data,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> sendPushNotification(
|
||||
String token, String title, String body) async {
|
||||
try {
|
||||
http.Response response = await http.post(
|
||||
Uri.parse('https://fcm.googleapis.com/fcm/send'),
|
||||
headers: <String, String>{
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
'Authorization':
|
||||
'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2',
|
||||
},
|
||||
body: jsonEncode(
|
||||
<String, dynamic>{
|
||||
'notification': <String, dynamic>{
|
||||
'body': body,
|
||||
'title': title,
|
||||
|
||||
},
|
||||
'priority': 'high',
|
||||
'data': <String, dynamic>{
|
||||
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
|
||||
'id': '1',
|
||||
'status': 'done',
|
||||
},
|
||||
'to': token,
|
||||
},
|
||||
),
|
||||
);
|
||||
response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
-168
@@ -1,181 +1,62 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/services.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:intl/date_symbol_data_local.dart';
|
||||
// import 'package:prosappco/src/authentication/authentication_repository.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/service_web.dart';
|
||||
// import 'package:prosappco/src/providers/user_provider.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/calendar.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/city.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/login/login.dart';
|
||||
// import 'package:firebase_core/firebase_core.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/login/login_email.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/my_services.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/my_services_pro.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/new_number.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/new_password.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/profession.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/professional_profile.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/professional_revision.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/profile/profile.dart';
|
||||
// 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/map/service.dart';
|
||||
// import 'package:prosappco/src/presentation/screens/solicitudes.dart';
|
||||
// import 'package:prosappco/src/services/local_notifications.dart';
|
||||
// import 'firebase_options.dart';
|
||||
// import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
// import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
// import 'package:provider/provider.dart';
|
||||
// import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
// Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
// await Firebase.initializeApp();
|
||||
|
||||
// print('Handling a backdround message: ${message.messageId}');
|
||||
// }
|
||||
|
||||
// void main() async {
|
||||
// WidgetsFlutterBinding.ensureInitialized();
|
||||
// await Firebase.initializeApp(
|
||||
// options: DefaultFirebaseOptions.currentPlatform,
|
||||
// ).then((value) => Get.put(AuthenticationRepository()));
|
||||
|
||||
// // Evitar la solicitud de permiso en la versión web
|
||||
// if (!kIsWeb) {
|
||||
// FirebaseMessaging messaging = FirebaseMessaging.instance;
|
||||
|
||||
// await messaging.requestPermission(
|
||||
// alert: true,
|
||||
// announcement: false,
|
||||
// badge: true,
|
||||
// carPlay: false,
|
||||
// criticalAlert: false,
|
||||
// provisional: false,
|
||||
// sound: true,
|
||||
// );
|
||||
|
||||
// FirebaseMessaging.onBackgroundMessage(
|
||||
// (_firebaseMessagingBackgroundHandler));
|
||||
|
||||
// FirebaseMessaging.onMessage.listen((RemoteMessage message) {});
|
||||
// }
|
||||
|
||||
// await initNotifications();
|
||||
|
||||
// await initializeDateFormatting('es_MX', null);
|
||||
|
||||
// SystemChrome.setPreferredOrientations([
|
||||
// DeviceOrientation.portraitUp,
|
||||
// DeviceOrientation.portraitDown,
|
||||
// ]);
|
||||
|
||||
// runApp(const MyApp());
|
||||
// }
|
||||
|
||||
// class MyApp extends StatelessWidget {
|
||||
// const MyApp({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return MultiProvider(
|
||||
// providers: [
|
||||
// ChangeNotifierProvider(
|
||||
// create: (_) => UserProvider(),
|
||||
// ),
|
||||
// ],
|
||||
// child: GetMaterialApp(
|
||||
// theme: ThemeData(fontFamily: 'Poppins'),
|
||||
// debugShowCheckedModeBanner: false,
|
||||
// localizationsDelegates: const [
|
||||
// GlobalMaterialLocalizations.delegate,
|
||||
// GlobalWidgetsLocalizations.delegate,
|
||||
// GlobalCupertinoLocalizations.delegate,
|
||||
// ],
|
||||
// supportedLocales: const [
|
||||
// Locale('es', 'US'),
|
||||
// ],
|
||||
// title: 'ProsApp',
|
||||
// initialRoute: '/',
|
||||
// routes: {
|
||||
// '/': (context) => const LoginScreen(),
|
||||
// '/splash': (context) => const SplashScreen(),
|
||||
// '/login': (context) => const LoginEmailScreen(),
|
||||
// '/profile': (context) => const ProfileScreen(),
|
||||
// '/register': (context) => const RegisterScreen(),
|
||||
// '/city': (context) => const CityScreen(),
|
||||
// '/profession': (context) => const ProfessionScreen(),
|
||||
// '/newNumber': (context) => const NewNumberScreen(),
|
||||
// '/profesionalRevision': (context) =>
|
||||
// const ProfessionalRevisionScreen(),
|
||||
// '/profesionalProfile': (context) => const ProfessionalProfileScreen(),
|
||||
// '/solicitudEnviada': (context) => const RequestSentScreen(),
|
||||
// '/nuevaPassword': (context) => NewPasswordScreen(),
|
||||
// '/servicio': (context) => const ServiceScreen(),
|
||||
// '/servicioWeb': (context) => const ServiceWebScreen(),
|
||||
// '/profilePro': (context) => const ProfileProScreen(),
|
||||
// '/calendar': (context) => const CalendarScreen(),
|
||||
// '/solicitud': (context) => SolicitudScreen(),
|
||||
// '/misserviciospro': (context) => MyServicesProScreen(),
|
||||
// '/misservicios': (context) => MyServicesScreen(),
|
||||
// '/resetpassword': (context) => const ResetPasswordScreen(),
|
||||
// },
|
||||
// onGenerateRoute: (settings) {
|
||||
// throw Exception('Ruta desconocida: ${settings.name}');
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// class SplashScreen extends StatefulWidget {
|
||||
// const SplashScreen({super.key});
|
||||
|
||||
// @override
|
||||
// State<SplashScreen> createState() => _SplashScreenState();
|
||||
// }
|
||||
|
||||
// class _SplashScreenState extends State<SplashScreen> {
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// if (kIsWeb) {
|
||||
// // Espera 3 segundos y luego redirige a LoginScreen
|
||||
// Future.delayed(const Duration(seconds: 3), () {
|
||||
// Navigator.pushReplacement(
|
||||
// context,
|
||||
// MaterialPageRoute(builder: (context) => const LoginScreen()),
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Scaffold(
|
||||
// backgroundColor: Colors.blue,
|
||||
// body: Center(
|
||||
// child: kIsWeb ? Image.asset('/images/splash.png') : const LoginScreen(),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:injector/injector.dart';
|
||||
import 'package:prosappco/app_view.dart';
|
||||
import 'package:prosappco/blocs/authentication_bloc/authentication_bloc.dart';
|
||||
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
|
||||
import 'package:prosappco/blocs/notification_bloc/notification_bloc.dart';
|
||||
import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart';
|
||||
import 'package:prosappco/blocs/professional_profile_bloc/professional_profile_bloc.dart';
|
||||
import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart';
|
||||
import 'package:prosappco/dependency/app_di.dart';
|
||||
import 'app.dart';
|
||||
import 'package:prosappco/local_notifications/local_notifications.dart';
|
||||
import 'simple_bloc_observer.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp();
|
||||
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
|
||||
|
||||
// await Firebase.initializeApp();
|
||||
await NotificationBloc.initializeFCM();
|
||||
await LocalNotifications.initializeLocalNotifications();
|
||||
|
||||
Bloc.observer = SimpleBlocObserver();
|
||||
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
AppDI().register();
|
||||
runApp(const MainApp());
|
||||
|
||||
runApp(
|
||||
MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<AuthenticationBloc>(
|
||||
create: (context) => Injector.appInstance.get<AuthenticationBloc>(),
|
||||
),
|
||||
BlocProvider<MyUserBloc>(
|
||||
create: (context) => Injector.appInstance.get<MyUserBloc>(),
|
||||
),
|
||||
BlocProvider<ProfileBloc>(
|
||||
create: (context) => Injector.appInstance.get<ProfileBloc>(),
|
||||
),
|
||||
BlocProvider<ProfessionalBloc>(
|
||||
create: (context) => Injector.appInstance.get<ProfessionalBloc>(),
|
||||
),
|
||||
BlocProvider<ProfessionalProfileBloc>(
|
||||
create: (context) =>
|
||||
Injector.appInstance.get<ProfessionalProfileBloc>(),
|
||||
),
|
||||
BlocProvider<NotificationBloc>(
|
||||
create: (context) => Injector.appInstance.get<NotificationBloc>(),
|
||||
)
|
||||
],
|
||||
child: BlocBuilder<MyUserBloc, MyUserState>(
|
||||
builder: (context, state) {
|
||||
return const SafeArea(child: MyAppView());
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// runApp(const MainApp());
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:injector/injector.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:prosappco/blocs/chat_bloc/chat_bloc.dart';
|
||||
import 'package:prosappco/components/general_reputation.dart';
|
||||
import 'package:prosappco/local_notifications/local_notifications.dart';
|
||||
import 'package:service_repository/service_repository.dart';
|
||||
import 'package:user_repository/user_repository.dart';
|
||||
|
||||
@@ -26,6 +27,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
final FocusNode _messageFocusNode = FocusNode();
|
||||
|
||||
List<dynamic>? _userInfo;
|
||||
DateTime? _lastNotificationTime;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -177,126 +179,8 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Indicador de carga opcional si se desea mostrar mientras se carga la información por primera vez.
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
// if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
// return const Center(child: CircularProgressIndicator());
|
||||
// } else {
|
||||
// if (snapshot.hasError) {
|
||||
// return Center(
|
||||
// child: Text('Error inesperado: ${snapshot.error}'),
|
||||
// );
|
||||
// } else {
|
||||
// final userInfo = snapshot.data![0] as MyUser;
|
||||
|
||||
// return Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
// color: const Color(0xFFD6F4FF),
|
||||
// alignment: Alignment.topCenter,
|
||||
// child: ListTile(
|
||||
// leading: Container(
|
||||
// width: 60,
|
||||
// height: 60,
|
||||
// decoration: BoxDecoration(
|
||||
// color: Colors.grey.shade300,
|
||||
// shape: BoxShape.circle,
|
||||
// image: userInfo.picture == null
|
||||
// ? null
|
||||
// : DecorationImage(
|
||||
// image: NetworkImage(
|
||||
// userInfo.picture ?? ''),
|
||||
// fit: BoxFit.contain,
|
||||
// ),
|
||||
// ),
|
||||
// child: userInfo.picture == null
|
||||
// ? Icon(
|
||||
// CupertinoIcons.person,
|
||||
// color: Colors.grey.shade400,
|
||||
// size: 40,
|
||||
// )
|
||||
// : null,
|
||||
// ),
|
||||
// title: Text(
|
||||
// userInfo.name ?? '',
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// style: const TextStyle(
|
||||
// fontSize: 15,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// ),
|
||||
// ),
|
||||
// subtitle: widget.service.userId ==
|
||||
// FirebaseAuth.instance.currentUser!.uid
|
||||
// ? GeneralReputation(
|
||||
// userId: widget.service.professionalId,
|
||||
// builder: (context, reputation) {
|
||||
// final average = reputation.averagePro;
|
||||
// final total = reputation.totalPro;
|
||||
|
||||
// return Row(
|
||||
// children: [
|
||||
// RatingBar.builder(
|
||||
// initialRating:
|
||||
// calculoRating(average),
|
||||
// minRating: 1,
|
||||
// direction: Axis.horizontal,
|
||||
// allowHalfRating: true,
|
||||
// itemCount: 5,
|
||||
// itemSize: 25,
|
||||
// maxRating: 5,
|
||||
// itemBuilder: (context, _) =>
|
||||
// const Icon(
|
||||
// Icons.star,
|
||||
// color: Color(0xFF2BA4EC),
|
||||
// ),
|
||||
// onRatingUpdate: (rating) {},
|
||||
// ignoreGestures: true,
|
||||
// ),
|
||||
// const SizedBox(width: 5),
|
||||
// Text(
|
||||
// '${average.toStringAsFixed(1)} (${total.toString()})',
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// })
|
||||
// : GeneralReputation(
|
||||
// userId: widget.service.userId,
|
||||
// builder: (context, reputation) {
|
||||
// final average = reputation.average;
|
||||
// final total = reputation.total;
|
||||
|
||||
// return Row(
|
||||
// children: [
|
||||
// RatingBar.builder(
|
||||
// initialRating:
|
||||
// calculoRating(average),
|
||||
// minRating: 1,
|
||||
// direction: Axis.horizontal,
|
||||
// allowHalfRating: true,
|
||||
// itemCount: 5,
|
||||
// itemSize: 25,
|
||||
// maxRating: 5,
|
||||
// itemBuilder: (context, _) =>
|
||||
// const Icon(
|
||||
// Icons.star,
|
||||
// color: Color(0xFF2BA4EC),
|
||||
// ),
|
||||
// onRatingUpdate: (rating) {},
|
||||
// ignoreGestures: true,
|
||||
// ),
|
||||
// const SizedBox(width: 5),
|
||||
// Text(
|
||||
// '${average.toStringAsFixed(1)} (${total.toString()})',
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
@@ -353,6 +237,26 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
serviceId: widget.service.id!,
|
||||
message: message,
|
||||
));
|
||||
|
||||
if (_userInfo != null) {
|
||||
final userInfo = _userInfo![0] as MyUser;
|
||||
|
||||
if (userInfo.token == null) return;
|
||||
|
||||
if (_lastNotificationTime == null ||
|
||||
DateTime.now().difference(
|
||||
_lastNotificationTime!) >
|
||||
const Duration(minutes: 5)) {
|
||||
LocalNotifications.sendPushNotification(
|
||||
userInfo.token!,
|
||||
'Nuevo mensaje',
|
||||
'Tienes un nuevo mensaje de ${userInfo.name}',
|
||||
);
|
||||
|
||||
_lastNotificationTime = DateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
_messageController.clear();
|
||||
_messageFocusNode.requestFocus();
|
||||
},
|
||||
@@ -375,6 +279,26 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
serviceId: widget.service.id!,
|
||||
message: message,
|
||||
));
|
||||
|
||||
if (_userInfo != null) {
|
||||
final userInfo = _userInfo![0] as MyUser;
|
||||
|
||||
if (userInfo.token == null) return;
|
||||
|
||||
if (_lastNotificationTime == null ||
|
||||
DateTime.now().difference(
|
||||
_lastNotificationTime!) >
|
||||
const Duration(minutes: 5)) {
|
||||
LocalNotifications.sendPushNotification(
|
||||
userInfo.token!,
|
||||
'Nuevo mensaje',
|
||||
'Tienes un nuevo mensaje de ${userInfo.name}',
|
||||
);
|
||||
|
||||
_lastNotificationTime = DateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
_messageController.clear();
|
||||
_messageFocusNode.requestFocus();
|
||||
},
|
||||
|
||||
@@ -180,7 +180,7 @@ class UserCalendarScreenState extends State<UserCalendarScreen> {
|
||||
schedule.range2Hour2!,
|
||||
);
|
||||
|
||||
return rangesItemList(ranges, _services);
|
||||
return rangesItemList(ranges, _services, today);
|
||||
}
|
||||
|
||||
List<TimeOfDay> ranges1 = TimeOfDayUtils.genRanges(
|
||||
@@ -193,26 +193,29 @@ class UserCalendarScreenState extends State<UserCalendarScreen> {
|
||||
);
|
||||
|
||||
return [
|
||||
...rangesItemList(ranges1, _services),
|
||||
...rangesItemList(ranges2, _services),
|
||||
...rangesItemList(ranges1, _services, today),
|
||||
...rangesItemList(ranges2, _services, today),
|
||||
];
|
||||
}
|
||||
|
||||
bool _isHora1Ocupada(TimeOfDay hora1, List<ServiceEntity>? events) {
|
||||
bool _isHora1Ocupada(
|
||||
TimeOfDay hora1, List<ServiceEntity>? events, DateTime selectedDay) {
|
||||
if (events != null) {
|
||||
for (ServiceEntity event in events) {
|
||||
if (hora1 == event.range1Hour1) {
|
||||
return true;
|
||||
if (selectedDay.toString() == event.day) {
|
||||
if (hora1 == event.range1Hour1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Widget> rangesItemList(
|
||||
List<TimeOfDay> ranges, List<ServiceEntity>? events) {
|
||||
List<Widget> rangesItemList(List<TimeOfDay> ranges,
|
||||
List<ServiceEntity>? events, DateTime selectedDay) {
|
||||
return ranges.map((time) {
|
||||
if (_isHora1Ocupada(time, events)) {
|
||||
if (_isHora1Ocupada(time, events, selectedDay)) {
|
||||
return Card(
|
||||
elevation: 4,
|
||||
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
|
||||
|
||||
@@ -13,9 +13,11 @@ import 'package:injector/injector.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:professional_repository/professional_repository.dart';
|
||||
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
|
||||
import 'package:prosappco/blocs/notification_bloc/notification_bloc.dart';
|
||||
import 'package:prosappco/blocs/professional_list_bloc/professional_list_bloc.dart';
|
||||
import 'package:prosappco/blocs/service_bloc/service_bloc.dart';
|
||||
import 'package:prosappco/constansts.dart';
|
||||
import 'package:prosappco/local_notifications/local_notifications.dart';
|
||||
import 'package:prosappco/screens/lists/professional_list_screen.dart';
|
||||
import 'package:prosappco/screens/user/user_service_screen.dart';
|
||||
import 'package:prosappco/utils/time_of_day_extension.dart';
|
||||
@@ -86,6 +88,10 @@ class _UserMapScreenState extends State<UserMapScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
// void requestNotificationPermission() async {
|
||||
// context.read<NotificationBloc>().requestPermission();
|
||||
// }
|
||||
|
||||
void _loadSettings() {
|
||||
settingRepository.getSettings().then(
|
||||
(value) => setState(() {
|
||||
@@ -172,10 +178,13 @@ class _UserMapScreenState extends State<UserMapScreen> {
|
||||
elevation: 3,
|
||||
minimumSize: const Size(50, 50),
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
Icons.menu,
|
||||
color: Colors.black,
|
||||
size: 35,
|
||||
size: context.select(
|
||||
(NotificationBloc bloc) =>
|
||||
bloc.state.status.index > 0 ? 35 : 35,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Scaffold.of(context).openDrawer();
|
||||
@@ -318,6 +327,9 @@ class _UserMapScreenState extends State<UserMapScreen> {
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: Icon(Icons.assignment_ind_rounded),
|
||||
suffixIcon: Icon(Icons.arrow_drop_down),
|
||||
// hintText: context.select(
|
||||
// (NotificationBloc bloc) => '${bloc.state.status}',
|
||||
// ),
|
||||
hintText: 'Selecciona un profesional',
|
||||
),
|
||||
),
|
||||
@@ -386,7 +398,7 @@ class _UserMapScreenState extends State<UserMapScreen> {
|
||||
onPressed: isLoading
|
||||
? null
|
||||
: () {
|
||||
// tODO: Crear servicio
|
||||
if (profesionalSeleccionado == null) return;
|
||||
if (serviceLocationPreference ==
|
||||
ServiceLocationPreferences.office) {
|
||||
if (settings?.tarifas == true) {
|
||||
@@ -487,7 +499,14 @@ class _UserMapScreenState extends State<UserMapScreen> {
|
||||
// TODO: error inesperado
|
||||
}
|
||||
|
||||
// clear inputs
|
||||
if (profesionalSeleccionado!.myUser.token != null) {
|
||||
LocalNotifications.sendPushNotification(
|
||||
profesionalSeleccionado!.myUser.token!,
|
||||
'Nuevo servicio',
|
||||
'Tienes una nueva solicitud de servicio pendiente',
|
||||
);
|
||||
}
|
||||
|
||||
fechaSeleccionada = null;
|
||||
horaSeleccionada = null;
|
||||
profesionalSeleccionado = null;
|
||||
@@ -538,7 +557,6 @@ class _UserMapScreenState extends State<UserMapScreen> {
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
//x
|
||||
child: const Icon(
|
||||
CupertinoIcons.xmark,
|
||||
color: Colors.white,
|
||||
|
||||
@@ -12,6 +12,7 @@ class MyUserEntity extends Equatable {
|
||||
final String? birthday;
|
||||
final String? gender;
|
||||
final ProState proState;
|
||||
final String? token;
|
||||
|
||||
const MyUserEntity({
|
||||
required this.id,
|
||||
@@ -24,6 +25,7 @@ class MyUserEntity extends Equatable {
|
||||
required this.birthday,
|
||||
required this.gender,
|
||||
required this.proState,
|
||||
required this.token,
|
||||
});
|
||||
|
||||
Map<String, Object?> toDocument() {
|
||||
@@ -38,6 +40,7 @@ class MyUserEntity extends Equatable {
|
||||
'birthday': birthday,
|
||||
'gender': gender,
|
||||
'professional_state': enumToInt(proState),
|
||||
'token': token
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,12 +56,24 @@ class MyUserEntity extends Equatable {
|
||||
birthday: doc['birthday'] as String?,
|
||||
gender: doc['gender'] as String?,
|
||||
proState: intToEnum(doc['professional_state'] as int),
|
||||
token: doc['token'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[id, email, phone, name, nickname, city, picture, birthday, gender];
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
email,
|
||||
phone,
|
||||
name,
|
||||
nickname,
|
||||
city,
|
||||
picture,
|
||||
birthday,
|
||||
gender,
|
||||
proState,
|
||||
token
|
||||
];
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -72,7 +87,8 @@ class MyUserEntity extends Equatable {
|
||||
picture: $picture
|
||||
birthday: $birthday
|
||||
gender: $gender,
|
||||
proState: ${proState.name}
|
||||
proState: ${proState.name},
|
||||
token: $token
|
||||
}''';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ class MyUser extends Equatable {
|
||||
final String? birthday;
|
||||
final String? gender;
|
||||
final ProState proState;
|
||||
final String? token;
|
||||
|
||||
const MyUser({
|
||||
required this.id,
|
||||
@@ -24,6 +25,7 @@ class MyUser extends Equatable {
|
||||
this.birthday,
|
||||
this.gender,
|
||||
required this.proState,
|
||||
this.token,
|
||||
});
|
||||
|
||||
get drawerLabel => email != null && email!.isNotEmpty
|
||||
@@ -44,6 +46,7 @@ class MyUser extends Equatable {
|
||||
birthday: '',
|
||||
gender: '',
|
||||
proState: ProState.inactive,
|
||||
token: '',
|
||||
);
|
||||
|
||||
/// Modify MyUser parameters
|
||||
@@ -58,6 +61,7 @@ class MyUser extends Equatable {
|
||||
String? birthday,
|
||||
String? gender,
|
||||
ProState? proState,
|
||||
String? token,
|
||||
}) {
|
||||
return MyUser(
|
||||
id: id ?? this.id,
|
||||
@@ -70,6 +74,7 @@ class MyUser extends Equatable {
|
||||
birthday: birthday ?? this.birthday,
|
||||
gender: gender ?? this.gender,
|
||||
proState: proState ?? this.proState,
|
||||
token: token ?? this.token,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,6 +96,7 @@ class MyUser extends Equatable {
|
||||
birthday: birthday,
|
||||
gender: gender,
|
||||
proState: proState,
|
||||
token: token,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,6 +112,7 @@ class MyUser extends Equatable {
|
||||
birthday: entity.birthday,
|
||||
gender: entity.gender,
|
||||
proState: entity.proState,
|
||||
token: entity.token,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,5 +128,6 @@ class MyUser extends Equatable {
|
||||
birthday,
|
||||
gender,
|
||||
proState,
|
||||
token,
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user