score
This commit is contained in:
+5
-4
@@ -23,9 +23,9 @@ class MainApp extends StatelessWidget {
|
|||||||
BlocProvider<MyUserBloc>(
|
BlocProvider<MyUserBloc>(
|
||||||
create: (context) => Injector.appInstance.get<MyUserBloc>(),
|
create: (context) => Injector.appInstance.get<MyUserBloc>(),
|
||||||
),
|
),
|
||||||
BlocProvider<ScoreBloc>(
|
// BlocProvider<ScoreBloc>(
|
||||||
create: (context) => Injector.appInstance.get(),
|
// create: (context) => Injector.appInstance.get(),
|
||||||
),
|
// ),
|
||||||
BlocProvider<ProfileBloc>(
|
BlocProvider<ProfileBloc>(
|
||||||
create: (context) => Injector.appInstance.get<ProfileBloc>(),
|
create: (context) => Injector.appInstance.get<ProfileBloc>(),
|
||||||
),
|
),
|
||||||
@@ -33,7 +33,8 @@ class MainApp extends StatelessWidget {
|
|||||||
create: (context) => Injector.appInstance.get<ProfessionalBloc>(),
|
create: (context) => Injector.appInstance.get<ProfessionalBloc>(),
|
||||||
),
|
),
|
||||||
BlocProvider<ProfessionalProfileBloc>(
|
BlocProvider<ProfessionalProfileBloc>(
|
||||||
create: (context) => Injector.appInstance.get<ProfessionalProfileBloc>(),
|
create: (context) =>
|
||||||
|
Injector.appInstance.get<ProfessionalProfileBloc>(),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
child: BlocBuilder<MyUserBloc, MyUserState>(
|
child: BlocBuilder<MyUserBloc, MyUserState>(
|
||||||
|
|||||||
@@ -3,23 +3,27 @@ import 'dart:developer';
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:score_repository/score_repository.dart';
|
import 'package:score_repository/score_repository.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:user_repository/user_repository.dart';
|
||||||
|
|
||||||
part 'score_event.dart';
|
part 'score_event.dart';
|
||||||
part 'score_state.dart';
|
part 'score_state.dart';
|
||||||
|
|
||||||
class ScoreBloc extends Bloc<ScoreEvent, ScoreState> {
|
class ScoreBloc extends Bloc<ScoreEvent, ScoreState> {
|
||||||
final FirebaseScoreRepository _firebaseScoreRepository;
|
final FirebaseScoreRepository _scoreRepository;
|
||||||
|
final UserRepository _userRepository;
|
||||||
|
|
||||||
ScoreBloc({
|
ScoreBloc({
|
||||||
required FirebaseScoreRepository firebaseScoreRepository,
|
required FirebaseScoreRepository scoreRepository,
|
||||||
}) : _firebaseScoreRepository = firebaseScoreRepository,
|
required UserRepository userRepository,
|
||||||
|
}) : _scoreRepository = scoreRepository,
|
||||||
|
_userRepository = userRepository,
|
||||||
super(ScoreInitial()) {
|
super(ScoreInitial()) {
|
||||||
try {
|
try {
|
||||||
emit(ScoreSuccess(_firebaseScoreRepository.getReputation()));
|
emit(ScoreSuccess(_scoreRepository.getReputation()));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
}
|
}
|
||||||
_firebaseScoreRepository.streamReputation().listen((event) {
|
_scoreRepository.streamReputation().listen((event) {
|
||||||
try {
|
try {
|
||||||
emit(ScoreSuccess(event));
|
emit(ScoreSuccess(event));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -29,13 +33,91 @@ class ScoreBloc extends Bloc<ScoreEvent, ScoreState> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
on<SendScoreEvent>(_onSendScoreEvent);
|
on<SendScoreEvent>(_onSendScoreEvent);
|
||||||
|
on<LoadScoresForUserEvent>(_onLoadScoresForUserEvent);
|
||||||
|
on<LoadScoresForProfessionalEvent>(_onLoadScoresForProfessionalEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSendScoreEvent(SendScoreEvent event, Emitter<ScoreState> emit) async {
|
void _onSendScoreEvent(SendScoreEvent event, Emitter<ScoreState> emit) async {
|
||||||
try {
|
try {
|
||||||
await _firebaseScoreRepository.addComment(event.comment);
|
await _scoreRepository.addComment(event.comment);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onLoadScoresForUserEvent(
|
||||||
|
LoadScoresForUserEvent event, Emitter<ScoreState> emit) async {
|
||||||
|
try {
|
||||||
|
final scoresStream = _scoreRepository.getScoresForUser(event.userId);
|
||||||
|
|
||||||
|
await for (var scores in scoresStream) {
|
||||||
|
if (scores.isEmpty) {
|
||||||
|
emit(const ScoresForUserLoaded([]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final userIds = scores.map((e) => e.authorId);
|
||||||
|
|
||||||
|
final List<MyUser> users =
|
||||||
|
await _userRepository.getUsersFromIds(userIds);
|
||||||
|
|
||||||
|
final usersDir = {for (var e in users) e.id: e};
|
||||||
|
|
||||||
|
final scoresInfo = scores.map((e) {
|
||||||
|
return ScoreInfoUI(
|
||||||
|
score: e,
|
||||||
|
user: usersDir[e.authorId]!,
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
log(scoresInfo.toString());
|
||||||
|
|
||||||
|
emit(ScoresForUserLoaded(scoresInfo));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
emit(ScoreFailure());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onLoadScoresForProfessionalEvent(
|
||||||
|
LoadScoresForProfessionalEvent event,
|
||||||
|
Emitter<ScoreState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final scoresStream =
|
||||||
|
_scoreRepository.getScoresForProfessional(event.userId);
|
||||||
|
|
||||||
|
await for (var scores in scoresStream) {
|
||||||
|
if (scores.isEmpty) {
|
||||||
|
emit(const ScoresForUserLoaded([]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final userIds = scores.map((e) => e.authorId);
|
||||||
|
|
||||||
|
final List<MyUser> users =
|
||||||
|
await _userRepository.getUsersFromIds(userIds);
|
||||||
|
|
||||||
|
final usersDir = {for (var e in users) e.id: e};
|
||||||
|
|
||||||
|
final scoresInfo = scores.map((e) {
|
||||||
|
return ScoreInfoUI(
|
||||||
|
score: e,
|
||||||
|
user: usersDir[e.authorId]!,
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
emit(ScoresForUserLoaded(scoresInfo));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
emit(ScoreFailure());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScoreInfoUI {
|
||||||
|
final CommentEntity score;
|
||||||
|
final MyUser user;
|
||||||
|
|
||||||
|
ScoreInfoUI({required this.score, required this.user});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,3 +15,21 @@ class SendScoreEvent extends ScoreEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [comment];
|
List<Object> get props => [comment];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LoadScoresForUserEvent extends ScoreEvent {
|
||||||
|
final String userId;
|
||||||
|
|
||||||
|
const LoadScoresForUserEvent({required this.userId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [userId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadScoresForProfessionalEvent extends ScoreEvent {
|
||||||
|
final String userId;
|
||||||
|
|
||||||
|
const LoadScoresForProfessionalEvent({required this.userId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [userId];
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,3 +20,12 @@ class ScoreSuccess extends ScoreState {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [reputation];
|
List<Object> get props => [reputation];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ScoresForUserLoaded extends ScoreState {
|
||||||
|
final List<ScoreInfoUI> scores;
|
||||||
|
|
||||||
|
const ScoresForUserLoaded(this.scores);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [scores];
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:injector/injector.dart';
|
||||||
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
|
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
|
||||||
import 'package:score_repository/score_repository.dart';
|
import 'package:score_repository/score_repository.dart';
|
||||||
|
|
||||||
@@ -10,14 +11,17 @@ class DrawerReputation extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<ScoreBloc, ScoreState>(builder: (context, state) {
|
return BlocProvider(
|
||||||
if (state is ScoreSuccess) {
|
create: (context) => Injector.appInstance.get<ScoreBloc>(),
|
||||||
return builder(state.reputation);
|
child: BlocBuilder<ScoreBloc, ScoreState>(builder: (context, state) {
|
||||||
} else if (state is ScoreFailure) {
|
if (state is ScoreSuccess) {
|
||||||
return const Center(child: Text('Error'));
|
return builder(state.reputation);
|
||||||
} else {
|
} else if (state is ScoreFailure) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: Text('Error'));
|
||||||
}
|
} else {
|
||||||
});
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+254
-232
@@ -6,16 +6,20 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||||
|
import 'package:injector/injector.dart';
|
||||||
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
|
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
|
||||||
import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart';
|
import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart';
|
||||||
|
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
|
||||||
import 'package:prosappco/components/drawer_reputation.dart';
|
import 'package:prosappco/components/drawer_reputation.dart';
|
||||||
import 'package:prosappco/components/general_drawer_header.dart';
|
import 'package:prosappco/components/general_drawer_header.dart';
|
||||||
import 'package:prosappco/components/general_drawer_item.dart';
|
import 'package:prosappco/components/general_drawer_item.dart';
|
||||||
import 'package:prosappco/screens/configuration/configuration_screen.dart';
|
import 'package:prosappco/screens/configuration/configuration_screen.dart';
|
||||||
import 'package:prosappco/screens/configuration/configuration_support_screen.dart';
|
import 'package:prosappco/screens/configuration/configuration_support_screen.dart';
|
||||||
import 'package:prosappco/screens/lists/professional_list_screen.dart';
|
import 'package:prosappco/screens/lists/professional_list_screen.dart';
|
||||||
|
import 'package:prosappco/screens/lists/professional_score_list_screen.dart';
|
||||||
import 'package:prosappco/screens/lists/professional_service_history_list_screen.dart';
|
import 'package:prosappco/screens/lists/professional_service_history_list_screen.dart';
|
||||||
import 'package:prosappco/screens/lists/professional_service_list_screen.dart';
|
import 'package:prosappco/screens/lists/professional_service_list_screen.dart';
|
||||||
|
import 'package:prosappco/screens/lists/user_score_list_screen.dart';
|
||||||
import 'package:prosappco/screens/lists/user_service_history_list_screen.dart';
|
import 'package:prosappco/screens/lists/user_service_history_list_screen.dart';
|
||||||
import 'package:prosappco/screens/lists/user_service_list_screen.dart';
|
import 'package:prosappco/screens/lists/user_service_list_screen.dart';
|
||||||
import 'package:prosappco/screens/professional/professional_calendar_screen.dart';
|
import 'package:prosappco/screens/professional/professional_calendar_screen.dart';
|
||||||
@@ -47,252 +51,274 @@ class GeneralDrawer extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<MyUserBloc, MyUserState>(
|
return BlocProvider(
|
||||||
builder: (context, userState) {
|
create: (context) => Injector.appInstance.get<ScoreBloc>(),
|
||||||
return BlocBuilder<ProfessionalBloc, ProfessionalState>(
|
child: BlocBuilder<MyUserBloc, MyUserState>(
|
||||||
builder: (context, professionalState) {
|
builder: (context, userState) {
|
||||||
return Drawer(
|
return BlocBuilder<ProfessionalBloc, ProfessionalState>(
|
||||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
builder: (context, professionalState) {
|
||||||
child: Column(
|
return Drawer(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
children: [
|
child: Column(
|
||||||
const GeneralDrawerHeader(),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
Divider(
|
children: [
|
||||||
color: Theme.of(context)
|
const GeneralDrawerHeader(),
|
||||||
.colorScheme
|
Divider(
|
||||||
.onSurface
|
color: Theme.of(context)
|
||||||
.withOpacity(0.1),
|
.colorScheme
|
||||||
thickness: 0.5,
|
.onSurface
|
||||||
height: 1,
|
.withOpacity(0.1),
|
||||||
),
|
thickness: 0.5,
|
||||||
Expanded(
|
height: 1,
|
||||||
child: SingleChildScrollView(
|
),
|
||||||
child: Column(
|
Expanded(
|
||||||
children: [
|
child: SingleChildScrollView(
|
||||||
shouldProModeActive(context, professionalState)
|
child: Column(
|
||||||
? GeneralDrawerItem(
|
children: [
|
||||||
leading: Icons.person_outline_rounded,
|
shouldProModeActive(context, professionalState)
|
||||||
label: 'Perfil profesional',
|
? GeneralDrawerItem(
|
||||||
onTap: () {
|
leading: Icons.person_outline_rounded,
|
||||||
Navigator.push(
|
label: 'Perfil profesional',
|
||||||
context,
|
onTap: () {
|
||||||
CupertinoPageRoute(
|
Navigator.push(
|
||||||
builder: (context) =>
|
context,
|
||||||
const ProfessionalProfileScreen(),
|
CupertinoPageRoute(
|
||||||
),
|
builder: (context) =>
|
||||||
);
|
const ProfessionalProfileScreen(),
|
||||||
},
|
),
|
||||||
)
|
);
|
||||||
: const SizedBox(),
|
},
|
||||||
GeneralDrawerItem(
|
)
|
||||||
leading: Icons.checklist_outlined,
|
: const SizedBox(),
|
||||||
label: 'Mis servicios',
|
GeneralDrawerItem(
|
||||||
onTap: () {
|
leading: Icons.checklist_outlined,
|
||||||
shouldProModeActive(context, professionalState)
|
label: 'Mis servicios',
|
||||||
? Navigator.push(
|
onTap: () {
|
||||||
context,
|
shouldProModeActive(context, professionalState)
|
||||||
CupertinoPageRoute(
|
? Navigator.push(
|
||||||
builder: (context) =>
|
context,
|
||||||
const ProfessionalServiceListScreen(),
|
CupertinoPageRoute(
|
||||||
// const UserServicesScreen(),
|
builder: (context) =>
|
||||||
),
|
const ProfessionalServiceListScreen(),
|
||||||
)
|
// const UserServicesScreen(),
|
||||||
: Navigator.push(
|
),
|
||||||
context,
|
)
|
||||||
CupertinoPageRoute(
|
: Navigator.push(
|
||||||
builder: (context) =>
|
context,
|
||||||
const UserServiceListScreen(),
|
CupertinoPageRoute(
|
||||||
// const UserServicesScreen(),
|
builder: (context) =>
|
||||||
),
|
const UserServiceListScreen(),
|
||||||
);
|
// const UserServicesScreen(),
|
||||||
},
|
),
|
||||||
),
|
);
|
||||||
GeneralDrawerItem(
|
},
|
||||||
leading: Icons.access_time_outlined,
|
),
|
||||||
label: 'Historial',
|
GeneralDrawerItem(
|
||||||
onTap: () {
|
leading: Icons.access_time_outlined,
|
||||||
shouldProModeActive(context, professionalState)
|
label: 'Historial',
|
||||||
? Navigator.push(
|
onTap: () {
|
||||||
context,
|
shouldProModeActive(context, professionalState)
|
||||||
CupertinoPageRoute(
|
? Navigator.push(
|
||||||
builder: (context) =>
|
context,
|
||||||
const ProfessionalServiceHistoryListScreen(),
|
CupertinoPageRoute(
|
||||||
),
|
builder: (context) =>
|
||||||
)
|
const ProfessionalServiceHistoryListScreen(),
|
||||||
: Navigator.push(
|
),
|
||||||
context,
|
)
|
||||||
CupertinoPageRoute(
|
: Navigator.push(
|
||||||
builder: (context) =>
|
context,
|
||||||
const UserServiceHistoryListScreen(),
|
CupertinoPageRoute(
|
||||||
),
|
builder: (context) =>
|
||||||
);
|
const UserServiceHistoryListScreen(),
|
||||||
},
|
),
|
||||||
),
|
);
|
||||||
shouldProModeActive(context, professionalState)
|
},
|
||||||
? GeneralDrawerItem(
|
),
|
||||||
onTap: () {
|
shouldProModeActive(context, professionalState)
|
||||||
Navigator.push(
|
? GeneralDrawerItem(
|
||||||
context,
|
onTap: () {
|
||||||
CupertinoPageRoute(
|
Navigator.push(
|
||||||
builder: (context) =>
|
context,
|
||||||
const ProfessionalCalendarScreen(),
|
CupertinoPageRoute(
|
||||||
),
|
builder: (context) =>
|
||||||
);
|
const ProfessionalCalendarScreen(),
|
||||||
},
|
),
|
||||||
label: 'Calendario',
|
);
|
||||||
leading: Icons.calendar_month_outlined,
|
},
|
||||||
)
|
label: 'Calendario',
|
||||||
: const SizedBox(),
|
leading: Icons.calendar_month_outlined,
|
||||||
GeneralDrawerItem(
|
)
|
||||||
leading: Icons.settings_outlined,
|
: const SizedBox(),
|
||||||
label: 'Configuración',
|
GeneralDrawerItem(
|
||||||
onTap: () {
|
leading: Icons.settings_outlined,
|
||||||
Navigator.push(
|
label: 'Configuración',
|
||||||
context,
|
onTap: () {
|
||||||
CupertinoPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
const ConfigurationScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GeneralDrawerItem(
|
|
||||||
leading: Icons.help_outline,
|
|
||||||
label: 'Soporte',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
CupertinoPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
const ConfigurationSupportScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GeneralDrawerItem(
|
|
||||||
leading: Icons.campaign_outlined,
|
|
||||||
label: 'Sugerencias',
|
|
||||||
onTap: () {
|
|
||||||
if (kIsWeb) {
|
|
||||||
_irSugerencias();
|
|
||||||
} else {
|
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
CupertinoPageRoute(
|
CupertinoPageRoute(
|
||||||
builder: (BuildContext context) {
|
builder: (context) =>
|
||||||
return WebViewScreen(
|
const ConfigurationScreen(),
|
||||||
label: 'Sugerencias',
|
|
||||||
link:
|
|
||||||
'https://admin.prosapp.co/sugerencias');
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
color: const Color(0xFF2BA4EC),
|
|
||||||
child: ListTile(
|
|
||||||
onTap: () {
|
|
||||||
shouldProModeActive(context, professionalState)
|
|
||||||
? Navigator.pop(context)
|
|
||||||
: Navigator.pop(context);
|
|
||||||
},
|
},
|
||||||
trailing: const Icon(
|
),
|
||||||
Icons.keyboard_arrow_right,
|
GeneralDrawerItem(
|
||||||
color: Colors.white,
|
leading: Icons.help_outline,
|
||||||
),
|
label: 'Soporte',
|
||||||
title: Text(
|
onTap: () {
|
||||||
shouldProModeActive(context, professionalState)
|
Navigator.push(
|
||||||
? 'Solicitudes'
|
context,
|
||||||
: 'Solicitar servicio',
|
CupertinoPageRoute(
|
||||||
style: const TextStyle(
|
builder: (context) =>
|
||||||
fontSize: 16,
|
const ConfigurationSupportScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GeneralDrawerItem(
|
||||||
|
leading: Icons.campaign_outlined,
|
||||||
|
label: 'Sugerencias',
|
||||||
|
onTap: () {
|
||||||
|
if (kIsWeb) {
|
||||||
|
_irSugerencias();
|
||||||
|
} else {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
CupertinoPageRoute(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return WebViewScreen(
|
||||||
|
label: 'Sugerencias',
|
||||||
|
link:
|
||||||
|
'https://admin.prosapp.co/sugerencias');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
color: const Color(0xFF2BA4EC),
|
||||||
|
child: ListTile(
|
||||||
|
onTap: () {
|
||||||
|
shouldProModeActive(
|
||||||
|
context, professionalState)
|
||||||
|
? Navigator.pop(context)
|
||||||
|
: Navigator.pop(context);
|
||||||
|
},
|
||||||
|
trailing: const Icon(
|
||||||
|
Icons.keyboard_arrow_right,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontWeight: FontWeight.bold,
|
),
|
||||||
|
title: Text(
|
||||||
|
shouldProModeActive(
|
||||||
|
context, professionalState)
|
||||||
|
? 'Solicitudes'
|
||||||
|
: 'Solicitar servicio',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
DrawerReputation(builder: (reputation) {
|
||||||
DrawerReputation(builder: (reputation) {
|
final isProModeActive =
|
||||||
final isProModeActive =
|
(professionalState is LoadedModeProState) &&
|
||||||
(professionalState is LoadedModeProState) &&
|
professionalState.isProModeActive;
|
||||||
professionalState.isProModeActive;
|
|
||||||
|
|
||||||
final total = isProModeActive
|
final total = isProModeActive
|
||||||
? reputation.totalPro
|
? reputation.totalPro
|
||||||
: reputation.total;
|
: reputation.total;
|
||||||
|
|
||||||
final average = isProModeActive
|
final average = isProModeActive
|
||||||
? reputation.averagePro
|
? reputation.averagePro
|
||||||
: reputation.average;
|
: reputation.average;
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {},
|
onTap: () {
|
||||||
trailing: const Icon(Icons.keyboard_arrow_right, color: Colors.black),
|
final isProModeActive = (professionalState
|
||||||
title: Row(
|
is LoadedModeProState) &&
|
||||||
children: [
|
professionalState.isProModeActive;
|
||||||
RatingBar.builder(
|
isProModeActive
|
||||||
initialRating: calculoRating(average),
|
? Navigator.push(context,
|
||||||
minRating: 1,
|
CupertinoPageRoute(
|
||||||
direction: Axis.horizontal,
|
builder: (context) {
|
||||||
allowHalfRating: true,
|
return const ProfessionalScoreListScreen();
|
||||||
itemCount: 5,
|
}))
|
||||||
itemSize: 25,
|
: Navigator.push(context,
|
||||||
maxRating: 5,
|
CupertinoPageRoute(
|
||||||
itemBuilder: (context, _) => const Icon(
|
builder: (context) {
|
||||||
Icons.star,
|
return const UserScoreListScreen();
|
||||||
color: Color(0xFF2BA4EC),
|
}));
|
||||||
|
},
|
||||||
|
trailing: const Icon(Icons.keyboard_arrow_right,
|
||||||
|
color: Colors.black),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
RatingBar.builder(
|
||||||
|
// initialRating: calculoRating(average),
|
||||||
|
initialRating: 3,
|
||||||
|
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,
|
||||||
),
|
),
|
||||||
onRatingUpdate: (rating) {},
|
const SizedBox(width: 5),
|
||||||
ignoreGestures: true,
|
Text(
|
||||||
),
|
'${average.toStringAsFixed(1)} (${total.toString()})',
|
||||||
const SizedBox(width: 5),
|
),
|
||||||
Text(
|
],
|
||||||
'${average.toStringAsFixed(1)} (${total.toString()})',
|
),
|
||||||
),
|
);
|
||||||
],
|
}),
|
||||||
),
|
const Divider(
|
||||||
);
|
height: 1,
|
||||||
}),
|
thickness: 0.5,
|
||||||
const Divider(
|
),
|
||||||
height: 1,
|
Padding(
|
||||||
thickness: 0.5,
|
padding: const EdgeInsets.only(top: 10),
|
||||||
),
|
child: Text(
|
||||||
Padding(
|
'Prosapp ® todos los derechos reservados',
|
||||||
padding: const EdgeInsets.only(top: 10),
|
style: TextStyle(
|
||||||
child: Text(
|
fontSize: 10,
|
||||||
'Prosapp ® todos los derechos reservados',
|
color: Colors.grey[700],
|
||||||
style: TextStyle(
|
),
|
||||||
fontSize: 10,
|
|
||||||
color: Colors.grey[700],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Divider(
|
||||||
Divider(
|
color: Theme.of(context)
|
||||||
color: Theme.of(context)
|
.colorScheme
|
||||||
.colorScheme
|
.onSurface
|
||||||
.onSurface
|
.withOpacity(0.1),
|
||||||
.withOpacity(0.1),
|
thickness: 0.5,
|
||||||
thickness: 0.5,
|
height: 1,
|
||||||
height: 1,
|
),
|
||||||
),
|
const SizedBox(height: 15),
|
||||||
const SizedBox(height: 15),
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
child: buttonOfState(context, professionalState),
|
||||||
child: buttonOfState(context, professionalState),
|
),
|
||||||
),
|
const SizedBox(height: 15),
|
||||||
const SizedBox(height: 15),
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,10 +415,6 @@ class GeneralDrawer extends StatelessWidget {
|
|||||||
'Modo profesional',
|
'Modo profesional',
|
||||||
style: TextStyle(color: Colors.white, fontSize: 18),
|
style: TextStyle(color: Colors.white, fontSize: 18),
|
||||||
),
|
),
|
||||||
|
|
||||||
// const CircularProgressIndicator(
|
|
||||||
// color: Colors.white,
|
|
||||||
// ),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ class AppDI {
|
|||||||
|
|
||||||
injector.registerSingleton<MyUserBloc>(
|
injector.registerSingleton<MyUserBloc>(
|
||||||
(() => MyUserBloc(myUserRepository: injector.get<UserRepository>())));
|
(() => MyUserBloc(myUserRepository: injector.get<UserRepository>())));
|
||||||
injector.registerSingleton(
|
|
||||||
(() => ScoreBloc(firebaseScoreRepository: injector.get())));
|
|
||||||
injector.registerSingleton<ProfileBloc>(
|
injector.registerSingleton<ProfileBloc>(
|
||||||
(() => ProfileBloc(userRepository: injector.get<UserRepository>())));
|
(() => ProfileBloc(userRepository: injector.get<UserRepository>())));
|
||||||
|
|
||||||
@@ -91,5 +90,17 @@ class AppDI {
|
|||||||
professionRepository: injector.get<FirebaseProfessionalRepository>(),
|
professionRepository: injector.get<FirebaseProfessionalRepository>(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
injector.registerDependency<ScoreBloc>(
|
||||||
|
() => ScoreBloc(
|
||||||
|
scoreRepository: injector.get<FirebaseScoreRepository>(),
|
||||||
|
userRepository: injector.get<UserRepository>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// injector.registerSingleton((() => ScoreBloc(
|
||||||
|
// firebaseScoreRepository: injector.get(),
|
||||||
|
// userRepository: injector.get<UserRepository>(),
|
||||||
|
// )));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ class _ProfessionalListScreenState extends State<ProfessionalListScreen> {
|
|||||||
.where((element) => removeDiacritics(element.myUser.name!)
|
.where((element) => removeDiacritics(element.myUser.name!)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.contains(removeDiacritics(_searchController.text.toLowerCase())))
|
.contains(removeDiacritics(_searchController.text.toLowerCase())))
|
||||||
.where((user) =>
|
// .where((user) =>
|
||||||
user.myUser.id != FirebaseAuth.instance.currentUser!.uid)
|
// user.myUser.id != FirebaseAuth.instance.currentUser!.uid)
|
||||||
.toList();
|
.toList();
|
||||||
// } else {
|
// } else {
|
||||||
// filteredUsers = state.users
|
// filteredUsers = state.users
|
||||||
@@ -183,12 +183,19 @@ class _ProfessionalListScreenState extends State<ProfessionalListScreen> {
|
|||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Row(
|
||||||
'${filteredUsers[index].myUser.name ?? ''}, ${filteredUsers[index].professionalInfo.profession}',
|
children: [
|
||||||
overflow: TextOverflow.ellipsis,
|
Flexible(
|
||||||
style: const TextStyle(
|
child: Text(
|
||||||
fontSize: 15,
|
'${filteredUsers[index].myUser.name ?? ''}, ',
|
||||||
),
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
filteredUsers[index].professionalInfo.profession,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
_disponibilidad(filteredUsers[index].professionalInfo),
|
_disponibilidad(filteredUsers[index].professionalInfo),
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:injector/injector.dart';
|
||||||
|
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
|
||||||
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
|
||||||
|
class ProfessionalScoreListScreen extends StatefulWidget {
|
||||||
|
const ProfessionalScoreListScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProfessionalScoreListScreen> createState() =>
|
||||||
|
_ProfessionalScoreListScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProfessionalScoreListScreenState
|
||||||
|
extends State<ProfessionalScoreListScreen> {
|
||||||
|
late final ScoreBloc scoreBloc;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
scoreBloc = Injector.appInstance.get<ScoreBloc>();
|
||||||
|
|
||||||
|
scoreBloc.add(LoadScoresForProfessionalEvent(
|
||||||
|
userId: FirebaseAuth.instance.currentUser!.uid));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => scoreBloc,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Reputación'),
|
||||||
|
),
|
||||||
|
body: BlocBuilder<ScoreBloc, ScoreState>(
|
||||||
|
builder: (context, scoreState) {
|
||||||
|
if (scoreState is ScoresForUserLoaded) {
|
||||||
|
return scoreState.scores.isEmpty
|
||||||
|
? const Center(
|
||||||
|
child: Text('No tienes calificaciones'),
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
|
itemCount: scoreState.scores.length,
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
final scoreInfo = scoreState.scores[index];
|
||||||
|
final score = scoreInfo.score;
|
||||||
|
final user = scoreInfo.user;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Colors.grey.withOpacity(0.2)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Container(
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
image: user.picture == null
|
||||||
|
? null
|
||||||
|
: DecorationImage(
|
||||||
|
image: NetworkImage(user.picture!),
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: user.picture == null
|
||||||
|
? Icon(
|
||||||
|
CupertinoIcons.person,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
size: 40,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
title: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
'${user.name}',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
'${score.createdAt.toDate().day}/${score.createdAt.toDate().month}/${score.createdAt.toDate().year}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'"${score.content.trim()}"',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
// icono de calificación
|
||||||
|
trailing: Text(
|
||||||
|
'⭐ ${score.score}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Shimmer.fromColors(
|
||||||
|
baseColor: Colors.grey[300]!,
|
||||||
|
highlightColor: Colors.grey[100]!,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: 10,
|
||||||
|
itemBuilder: (_, __) => ListTile(
|
||||||
|
leading: CircleAvatar(
|
||||||
|
backgroundColor: Colors.grey[300],
|
||||||
|
radius: 30,
|
||||||
|
),
|
||||||
|
title: Container(
|
||||||
|
height: 20,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Container(
|
||||||
|
height: 15,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:injector/injector.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
|
||||||
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
|
||||||
|
class UserScoreListScreen extends StatefulWidget {
|
||||||
|
const UserScoreListScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<UserScoreListScreen> createState() => _UserScoreListScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UserScoreListScreenState extends State<UserScoreListScreen> {
|
||||||
|
late final ScoreBloc scoreBloc;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
scoreBloc = Injector.appInstance.get<ScoreBloc>();
|
||||||
|
|
||||||
|
scoreBloc.add(
|
||||||
|
LoadScoresForUserEvent(userId: FirebaseAuth.instance.currentUser!.uid));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => scoreBloc,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Reputación'),
|
||||||
|
),
|
||||||
|
body: BlocBuilder<ScoreBloc, ScoreState>(
|
||||||
|
builder: (context, scoreState) {
|
||||||
|
if (scoreState is ScoresForUserLoaded) {
|
||||||
|
return scoreState.scores.isEmpty
|
||||||
|
? const Center(
|
||||||
|
child: Text('No tienes calificaciones'),
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
|
itemCount: scoreState.scores.length,
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
final scoreInfo = scoreState.scores[index];
|
||||||
|
final score = scoreInfo.score;
|
||||||
|
final user = scoreInfo.user;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Colors.grey.withOpacity(0.2)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Container(
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
image: user.picture == null
|
||||||
|
? null
|
||||||
|
: DecorationImage(
|
||||||
|
image: NetworkImage(user.picture!),
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: user.picture == null
|
||||||
|
? Icon(
|
||||||
|
CupertinoIcons.person,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
size: 40,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
title: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
'${user.name}',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
'${score.createdAt.toDate().day}/${score.createdAt.toDate().month}/${score.createdAt.toDate().year}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'"${score.content.trim()}"',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
// icono de calificación
|
||||||
|
trailing: Text(
|
||||||
|
'⭐ ${score.score}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Shimmer.fromColors(
|
||||||
|
baseColor: Colors.grey[300]!,
|
||||||
|
highlightColor: Colors.grey[100]!,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: 10,
|
||||||
|
itemBuilder: (_, __) => ListTile(
|
||||||
|
leading: CircleAvatar(
|
||||||
|
backgroundColor: Colors.grey[300],
|
||||||
|
radius: 30,
|
||||||
|
),
|
||||||
|
title: Container(
|
||||||
|
height: 20,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Container(
|
||||||
|
height: 15,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+306
-296
@@ -43,319 +43,329 @@ class _ScoreScreenState extends State<ScoreScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return BlocProvider(
|
||||||
appBar: AppBar(
|
create: (context) => Injector.appInstance.get<ScoreBloc>(),
|
||||||
title: const Text('Calificación'),
|
child: Scaffold(
|
||||||
),
|
appBar: AppBar(
|
||||||
body: Column(
|
title: const Text('Calificación'),
|
||||||
children: [
|
),
|
||||||
Expanded(
|
body: Column(
|
||||||
child: SingleChildScrollView(
|
children: [
|
||||||
child: Column(
|
Expanded(
|
||||||
children: [
|
child: SingleChildScrollView(
|
||||||
FutureBuilder(
|
child: Column(
|
||||||
future: _userInfoFuture,
|
children: [
|
||||||
builder: (context, AsyncSnapshot<List<dynamic>> snapshot) {
|
FutureBuilder(
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
future: _userInfoFuture,
|
||||||
return const Center(child: CircularProgressIndicator());
|
builder:
|
||||||
} else {
|
(context, AsyncSnapshot<List<dynamic>> snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.connectionState ==
|
||||||
return Center(
|
ConnectionState.waiting) {
|
||||||
child: Text('Error inesperado: ${snapshot.error}'),
|
return const Center(
|
||||||
);
|
child: CircularProgressIndicator());
|
||||||
} else {
|
} else {
|
||||||
final userInfo = snapshot.data![0] as MyUser;
|
if (snapshot.hasError) {
|
||||||
|
return Center(
|
||||||
|
child:
|
||||||
|
Text('Error inesperado: ${snapshot.error}'),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final userInfo = snapshot.data![0] as MyUser;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 95,
|
width: 95,
|
||||||
height: 95,
|
height: 95,
|
||||||
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: 50,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'${userInfo.name}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 15,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
GeneralReputation(
|
|
||||||
userId: userId,
|
|
||||||
builder: (BuildContext context,
|
|
||||||
ReputationEntity reputation) {
|
|
||||||
final double average;
|
|
||||||
|
|
||||||
if (isProfessional) {
|
|
||||||
average = reputation.averagePro;
|
|
||||||
} else {
|
|
||||||
average = reputation.average;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Positioned(
|
|
||||||
top: 3,
|
|
||||||
right: MediaQuery.of(context).size.width *
|
|
||||||
0.3,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 10,
|
|
||||||
vertical: 3,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.grey.shade300,
|
||||||
borderRadius:
|
shape: BoxShape.circle,
|
||||||
BorderRadius.circular(20),
|
image: userInfo.picture == null
|
||||||
boxShadow: [
|
? null
|
||||||
BoxShadow(
|
: DecorationImage(
|
||||||
color:
|
image: NetworkImage(
|
||||||
Colors.black.withOpacity(0.1),
|
userInfo.picture!),
|
||||||
spreadRadius: 1,
|
fit: BoxFit.contain,
|
||||||
blurRadius: 2,
|
),
|
||||||
offset: const Offset(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: userInfo.picture == null
|
||||||
mainAxisSize: MainAxisSize.min,
|
? Icon(
|
||||||
children: [
|
CupertinoIcons.person,
|
||||||
const Icon(
|
color: Colors.grey.shade400,
|
||||||
Icons.star,
|
size: 50,
|
||||||
color: Colors.yellow,
|
)
|
||||||
size: 20,
|
: null,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(height: 8),
|
||||||
Flexible(
|
Text(
|
||||||
child: Text(
|
'${userInfo.name}',
|
||||||
average.toStringAsFixed(2),
|
style: const TextStyle(
|
||||||
style: const TextStyle(
|
fontSize: 20,
|
||||||
fontSize: 15,
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GeneralReputation(
|
||||||
|
userId: userId,
|
||||||
|
builder: (BuildContext context,
|
||||||
|
ReputationEntity reputation) {
|
||||||
|
final double average;
|
||||||
|
|
||||||
|
if (isProfessional) {
|
||||||
|
average = reputation.averagePro;
|
||||||
|
} else {
|
||||||
|
average = reputation.average;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Positioned(
|
||||||
|
top: 3,
|
||||||
|
right:
|
||||||
|
MediaQuery.of(context).size.width *
|
||||||
|
0.3,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 3,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(20),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black
|
||||||
|
.withOpacity(0.1),
|
||||||
|
spreadRadius: 1,
|
||||||
|
blurRadius: 2,
|
||||||
|
offset: const Offset(
|
||||||
|
0,
|
||||||
|
1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.star,
|
||||||
|
color: Colors.yellow,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
average.toStringAsFixed(2),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
);
|
// Padding(
|
||||||
// Padding(
|
// padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
// padding: const EdgeInsets.symmetric(horizontal: 15),
|
// child: ListTile(
|
||||||
// child: ListTile(
|
// leading: Container(
|
||||||
// leading: Container(
|
// width: 60,
|
||||||
// width: 60,
|
// height: 60,
|
||||||
// height: 60,
|
// decoration: BoxDecoration(
|
||||||
// decoration: BoxDecoration(
|
// color: Colors.grey.shade300,
|
||||||
// color: Colors.grey.shade300,
|
// shape: BoxShape.circle,
|
||||||
// shape: BoxShape.circle,
|
// image: userInfo.picture == null
|
||||||
// image: userInfo.picture == null
|
// ? null
|
||||||
// ? null
|
// : DecorationImage(
|
||||||
// : DecorationImage(
|
// image: NetworkImage(
|
||||||
// image: NetworkImage(
|
// userInfo.picture ?? ''),
|
||||||
// userInfo.picture ?? ''),
|
// fit: BoxFit.contain,
|
||||||
// fit: BoxFit.contain,
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// child: userInfo.picture == null
|
||||||
// child: userInfo.picture == null
|
// ? Icon(
|
||||||
// ? Icon(
|
// CupertinoIcons.person,
|
||||||
// CupertinoIcons.person,
|
// color: Colors.grey.shade400,
|
||||||
// color: Colors.grey.shade400,
|
// size: 40,
|
||||||
// size: 40,
|
// )
|
||||||
// )
|
// : null,
|
||||||
// : null,
|
// ),
|
||||||
// ),
|
// title: Text(
|
||||||
// title: Text(
|
// userInfo.name ?? '',
|
||||||
// userInfo.name ?? '',
|
// overflow: TextOverflow.ellipsis,
|
||||||
// overflow: TextOverflow.ellipsis,
|
// style: const TextStyle(
|
||||||
// style: const TextStyle(
|
// fontSize: 15,
|
||||||
// fontSize: 15,
|
// fontWeight: FontWeight.w600,
|
||||||
// fontWeight: FontWeight.w600,
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// subtitle: const Text(
|
||||||
// subtitle: const Text(
|
// 'Rating: 5.0',
|
||||||
// 'Rating: 5.0',
|
// style: TextStyle(
|
||||||
// style: TextStyle(
|
// fontSize: 13,
|
||||||
// fontSize: 13,
|
// color: Colors.blue,
|
||||||
// color: Colors.blue,
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// );
|
||||||
// );
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
const Text(
|
|
||||||
'Califica el servicio',
|
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
RatingBar.builder(
|
|
||||||
initialRating: _rating,
|
|
||||||
minRating: 1,
|
|
||||||
direction: Axis.horizontal,
|
|
||||||
allowHalfRating: true,
|
|
||||||
itemCount: 5,
|
|
||||||
itemSize: 40,
|
|
||||||
glow: false,
|
|
||||||
maxRating: 5,
|
|
||||||
itemPadding: const EdgeInsets.symmetric(horizontal: 5),
|
|
||||||
itemBuilder: (context, _) => const Icon(
|
|
||||||
Icons.star,
|
|
||||||
color: Color(0xFF2BA4EC),
|
|
||||||
),
|
),
|
||||||
onRatingUpdate: (rating) {
|
const SizedBox(height: 10),
|
||||||
setState(() {
|
const Text(
|
||||||
_rating = rating;
|
'Califica el servicio',
|
||||||
});
|
style:
|
||||||
},
|
TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
|
||||||
ignoreGestures: false,
|
),
|
||||||
),
|
const SizedBox(height: 10),
|
||||||
Padding(
|
RatingBar.builder(
|
||||||
padding: const EdgeInsets.only(top: 8),
|
initialRating: _rating,
|
||||||
child: Text(
|
minRating: 1,
|
||||||
customMessage(_rating),
|
direction: Axis.horizontal,
|
||||||
style: const TextStyle(
|
allowHalfRating: true,
|
||||||
fontSize: 16,
|
itemCount: 5,
|
||||||
|
itemSize: 40,
|
||||||
|
glow: false,
|
||||||
|
maxRating: 5,
|
||||||
|
itemPadding: const EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
itemBuilder: (context, _) => const Icon(
|
||||||
|
Icons.star,
|
||||||
color: Color(0xFF2BA4EC),
|
color: Color(0xFF2BA4EC),
|
||||||
),
|
),
|
||||||
|
onRatingUpdate: (rating) {
|
||||||
|
setState(() {
|
||||||
|
_rating = rating;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
ignoreGestures: false,
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.only(top: 8),
|
||||||
padding: const EdgeInsets.symmetric(
|
child: Text(
|
||||||
horizontal: 40, vertical: 40),
|
customMessage(_rating),
|
||||||
child: Column(
|
style: const TextStyle(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
fontSize: 16,
|
||||||
children: [
|
color: Color(0xFF2BA4EC),
|
||||||
const Text(
|
),
|
||||||
'Comentario',
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 40, vertical: 40),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Comentario',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
maxLines: null,
|
||||||
|
maxLength: 400,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
controller: commentController,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 1,
|
||||||
|
thickness: 0.5,
|
||||||
|
),
|
||||||
|
BlocProvider<ServiceBloc>(
|
||||||
|
create: (context) => Injector.appInstance.get<ServiceBloc>(),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10,
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: BlocBuilder<ServiceBloc, ServiceState>(
|
||||||
|
builder: (context, serviceState) {
|
||||||
|
return FilledButton(
|
||||||
|
onPressed: () {
|
||||||
|
final CommentEntity comment = widget.service.userId ==
|
||||||
|
FirebaseAuth.instance.currentUser!.uid
|
||||||
|
? CommentEntity(
|
||||||
|
serviceId: widget.service.id!,
|
||||||
|
authorId:
|
||||||
|
FirebaseAuth.instance.currentUser!.uid,
|
||||||
|
isFromUser: true,
|
||||||
|
score: _rating,
|
||||||
|
destinationId: widget.service.professionalId,
|
||||||
|
content: commentController.text.trim(),
|
||||||
|
createdAt: Timestamp.now(),
|
||||||
|
)
|
||||||
|
: CommentEntity(
|
||||||
|
serviceId: widget.service.id!,
|
||||||
|
authorId:
|
||||||
|
FirebaseAuth.instance.currentUser!.uid,
|
||||||
|
isFromUser: false,
|
||||||
|
score: _rating,
|
||||||
|
destinationId: widget.service.userId,
|
||||||
|
content: commentController.text.trim(),
|
||||||
|
createdAt: Timestamp.now(),
|
||||||
|
);
|
||||||
|
|
||||||
|
BlocProvider.of<ScoreBloc>(context)
|
||||||
|
.add(SendScoreEvent(comment: comment));
|
||||||
|
|
||||||
|
if (widget.service.userId ==
|
||||||
|
FirebaseAuth.instance.currentUser!.uid) {
|
||||||
|
context.read<ServiceBloc>().add(
|
||||||
|
UpdateProfessionalScored(widget.service.id!));
|
||||||
|
} else {
|
||||||
|
context
|
||||||
|
.read<ServiceBloc>()
|
||||||
|
.add(UpdateUserScored(widget.service.id!));
|
||||||
|
}
|
||||||
|
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
width: double.infinity,
|
||||||
|
child: const Text(
|
||||||
|
'Enviar calificación',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20, fontWeight: FontWeight.w600),
|
color: Colors.white,
|
||||||
),
|
fontSize: 18,
|
||||||
TextFormField(
|
),
|
||||||
maxLines: null,
|
|
||||||
maxLength: 400,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
controller: commentController,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Divider(
|
|
||||||
height: 1,
|
|
||||||
thickness: 0.5,
|
|
||||||
),
|
|
||||||
BlocProvider<ServiceBloc>(
|
|
||||||
create: (context) => Injector.appInstance.get<ServiceBloc>(),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 10,
|
|
||||||
horizontal: 15,
|
|
||||||
),
|
|
||||||
child: BlocBuilder<ServiceBloc, ServiceState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return FilledButton(
|
|
||||||
onPressed: () {
|
|
||||||
final CommentEntity comment = widget.service.userId ==
|
|
||||||
FirebaseAuth.instance.currentUser!.uid
|
|
||||||
? CommentEntity(
|
|
||||||
serviceId: widget.service.id!,
|
|
||||||
authorId: FirebaseAuth.instance.currentUser!.uid,
|
|
||||||
isFromUser: true,
|
|
||||||
score: _rating,
|
|
||||||
destinationId: widget.service.professionalId,
|
|
||||||
content: commentController.text.trim(),
|
|
||||||
createdAt: Timestamp.now(),
|
|
||||||
)
|
|
||||||
: CommentEntity(
|
|
||||||
serviceId: widget.service.id!,
|
|
||||||
authorId: FirebaseAuth.instance.currentUser!.uid,
|
|
||||||
isFromUser: false,
|
|
||||||
score: _rating,
|
|
||||||
destinationId: widget.service.userId,
|
|
||||||
content: commentController.text.trim(),
|
|
||||||
createdAt: Timestamp.now(),
|
|
||||||
);
|
|
||||||
|
|
||||||
BlocProvider.of<ScoreBloc>(context)
|
|
||||||
.add(SendScoreEvent(comment: comment));
|
|
||||||
|
|
||||||
if (widget.service.userId ==
|
|
||||||
FirebaseAuth.instance.currentUser!.uid) {
|
|
||||||
context
|
|
||||||
.read<ServiceBloc>()
|
|
||||||
.add(UpdateProfessionalScored(widget.service.id!));
|
|
||||||
} else {
|
|
||||||
context
|
|
||||||
.read<ServiceBloc>()
|
|
||||||
.add(UpdateUserScored(widget.service.id!));
|
|
||||||
}
|
|
||||||
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
style: FilledButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
width: double.infinity,
|
|
||||||
child: const Text(
|
|
||||||
'Enviar calificación',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,26 @@ class FirebaseScoreRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<List<CommentEntity>> getScoresForUser(String userId) {
|
||||||
|
return commentsCollection
|
||||||
|
.where('destination_id', isEqualTo: userId)
|
||||||
|
.where('is_from_user', isEqualTo: false)
|
||||||
|
.snapshots()
|
||||||
|
.map((querySnapshot) => querySnapshot.docs
|
||||||
|
.map((doc) => CommentEntity.fromDocument(doc.data()))
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<CommentEntity>> getScoresForProfessional(String userId) {
|
||||||
|
return commentsCollection
|
||||||
|
.where('destination_id', isEqualTo: userId)
|
||||||
|
.where('is_from_user', isEqualTo: true)
|
||||||
|
.snapshots()
|
||||||
|
.map((querySnapshot) => querySnapshot.docs
|
||||||
|
.map((doc) => CommentEntity.fromDocument(doc.data()))
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
|
||||||
addComment(CommentEntity comment) async {
|
addComment(CommentEntity comment) async {
|
||||||
await commentsCollection.add(comment.toDocument());
|
await commentsCollection.add(comment.toDocument());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user