fix: 4 audit findings in professionals location flow
- Clear button (✕) now resets _cityMismatch and _detectedCity so banner and CTA unblock - _citiesMatch: replace bidirectional contains with equality + word-prefix check to prevent 'Cali' matching 'Calima' - ProfessionalsProvider: track _currentUserId to reset state on user change (cross-user leak) - ProfessionalsProvider: store _lastSearch so setLocationContext preserves active search term Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
f26b3d3c03
commit
54f9cc272e
+1
-1
@@ -50,7 +50,7 @@ class AppState extends StatelessWidget {
|
|||||||
ChangeNotifierProvider(create: (_) => ProfessionalFormProvider()),
|
ChangeNotifierProvider(create: (_) => ProfessionalFormProvider()),
|
||||||
ChangeNotifierProxyProvider<AuthProvider, ProfessionalsProvider>(
|
ChangeNotifierProxyProvider<AuthProvider, ProfessionalsProvider>(
|
||||||
create: (_) => ProfessionalsProvider(),
|
create: (_) => ProfessionalsProvider(),
|
||||||
update: (_, auth, prev) => prev!..updateCity(auth.user?.city),
|
update: (_, auth, prev) => prev!..updateCity(auth.user?.city, userId: auth.user?.id),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider(create: (_) => ServicesProvider()),
|
ChangeNotifierProvider(create: (_) => ServicesProvider()),
|
||||||
ChangeNotifierProvider(create: (_) => CalendarServicesProvider()),
|
ChangeNotifierProvider(create: (_) => CalendarServicesProvider()),
|
||||||
|
|||||||
@@ -6,12 +6,24 @@ class ProfessionalsProvider extends ChangeNotifier {
|
|||||||
List<UsuarioProfesional> professionals = [];
|
List<UsuarioProfesional> professionals = [];
|
||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
bool _initialized = false;
|
bool _initialized = false;
|
||||||
|
String? _currentUserId;
|
||||||
String? _locationCity;
|
String? _locationCity;
|
||||||
double? _locationLat;
|
double? _locationLat;
|
||||||
double? _locationLng;
|
double? _locationLng;
|
||||||
|
String? _lastSearch;
|
||||||
final _api = ApiService.instance;
|
final _api = ApiService.instance;
|
||||||
|
|
||||||
void updateCity(String? city) {
|
void updateCity(String? city, {String? userId}) {
|
||||||
|
// Reset state when a different user logs in (prevents cross-user data leak)
|
||||||
|
if (_currentUserId != userId) {
|
||||||
|
_currentUserId = userId;
|
||||||
|
_initialized = false;
|
||||||
|
_locationCity = null;
|
||||||
|
_locationLat = null;
|
||||||
|
_locationLng = null;
|
||||||
|
_lastSearch = null;
|
||||||
|
professionals = [];
|
||||||
|
}
|
||||||
if (!_initialized) {
|
if (!_initialized) {
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
_locationCity = city;
|
_locationCity = city;
|
||||||
@@ -23,15 +35,16 @@ class ProfessionalsProvider extends ChangeNotifier {
|
|||||||
if (city != null && city.isNotEmpty) _locationCity = city;
|
if (city != null && city.isNotEmpty) _locationCity = city;
|
||||||
_locationLat = lat;
|
_locationLat = lat;
|
||||||
_locationLng = lng;
|
_locationLng = lng;
|
||||||
getProfessionals();
|
getProfessionals(search: _lastSearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getProfessionals({String? search}) async {
|
Future<void> getProfessionals({String? search}) async {
|
||||||
|
if (search != null) _lastSearch = search.trim().isEmpty ? null : search.trim();
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
try {
|
try {
|
||||||
final params = <String, String>{};
|
final params = <String, String>{};
|
||||||
if (search != null && search.trim().isNotEmpty) params['search'] = search.trim();
|
if (_lastSearch != null && _lastSearch!.isNotEmpty) params['search'] = _lastSearch!;
|
||||||
if (_locationCity != null && _locationCity!.isNotEmpty) params['city'] = _locationCity!.trim();
|
if (_locationCity != null && _locationCity!.isNotEmpty) params['city'] = _locationCity!.trim();
|
||||||
if (_locationLat != null) params['lat'] = _locationLat.toString();
|
if (_locationLat != null) params['lat'] = _locationLat.toString();
|
||||||
if (_locationLng != null) params['lng'] = _locationLng.toString();
|
if (_locationLng != null) params['lng'] = _locationLng.toString();
|
||||||
|
|||||||
@@ -178,7 +178,8 @@ class _DashboardViewState extends State<DashboardView> {
|
|||||||
.replaceAll(RegExp(r'[úù]'), 'u');
|
.replaceAll(RegExp(r'[úù]'), 'u');
|
||||||
final na = normalize(a);
|
final na = normalize(a);
|
||||||
final nb = normalize(b);
|
final nb = normalize(b);
|
||||||
return na.contains(nb) || nb.contains(na);
|
// Exact match, or one is a full-word prefix of the other (handles "Bogotá D.C." vs "Bogotá")
|
||||||
|
return na == nb || na.startsWith('$nb ') || nb.startsWith('$na ');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _geocodeAndMoveMap(String address) async {
|
Future<void> _geocodeAndMoveMap(String address) async {
|
||||||
@@ -440,6 +441,8 @@ class _DashboardViewState extends State<DashboardView> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_suggestions = [];
|
_suggestions = [];
|
||||||
_currentAddress = '';
|
_currentAddress = '';
|
||||||
|
_detectedCity = '';
|
||||||
|
_cityMismatch = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user