From 739bc5391e882655c4f0cfa8a8919ea16b26c6ac Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:07:47 +0700 Subject: [PATCH] Fix review place loading state --- .../mapflow/presentation/mapflow_shell.dart | 208 +++++++++--------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/lib/features/mapflow/presentation/mapflow_shell.dart b/lib/features/mapflow/presentation/mapflow_shell.dart index 5b9ad09..2f01aa7 100644 --- a/lib/features/mapflow/presentation/mapflow_shell.dart +++ b/lib/features/mapflow/presentation/mapflow_shell.dart @@ -73,6 +73,7 @@ class _MapContentState extends State<_MapContent> { final selected = state.selectedPlace; final userCoordinate = state.userCoordinate; final targetCenter = _focusCenter(state); + final reviewCoordinate = userCoordinate ?? selected?.coordinate; final placeCubit = context.read(); final traitCounts = _countPlaceTraits(state.places); final availableTraits = [ @@ -98,7 +99,6 @@ class _MapContentState extends State<_MapContent> { userCoordinate: userCoordinate, onPlaceTap: (place) => _selectAndShowPlace(context, place), ), - if (targetCenter == null) const _MapTargetLoadingOverlay(), SafeArea( child: Align( alignment: Alignment.topLeft, @@ -118,10 +118,9 @@ class _MapContentState extends State<_MapContent> { child: Align( alignment: Alignment.topRight, child: _AddReviewAction( - onPressed: () => _openAddFlow( - context, - userCoordinate ?? selected?.coordinate, - ), + onPressed: reviewCoordinate == null + ? null + : () => _openAddFlow(context, reviewCoordinate), ), ), ), @@ -614,7 +613,7 @@ class _UserAvatar extends StatelessWidget { class _AddReviewAction extends StatelessWidget { const _AddReviewAction({required this.onPressed}); - final VoidCallback onPressed; + final VoidCallback? onPressed; @override Widget build(BuildContext context) { @@ -1515,49 +1514,6 @@ class _MapLoading extends StatelessWidget { } } -class _MapTargetLoadingOverlay extends StatelessWidget { - const _MapTargetLoadingOverlay(); - - @override - Widget build(BuildContext context) { - final tokens = context.mapflowTokens; - return IgnorePointer( - child: SafeArea( - child: Align( - alignment: Alignment.topCenter, - child: Padding( - padding: const EdgeInsets.only(top: 18), - child: DecoratedBox( - decoration: BoxDecoration( - color: tokens.mapPanel.withValues(alpha: 0.84), - borderRadius: BorderRadius.circular(999), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.24), - blurRadius: 22, - offset: const Offset(0, 10), - ), - ], - ), - child: Padding( - padding: const EdgeInsets.all(10), - child: SizedBox.square( - dimension: 28, - child: CircularProgressIndicator( - strokeWidth: 2.5, - color: tokens.voiceAccent, - backgroundColor: tokens.onDark.withValues(alpha: 0.12), - ), - ), - ), - ), - ), - ), - ), - ); - } -} - class _MapError extends StatelessWidget { const _MapError({required this.message}); @@ -2385,6 +2341,7 @@ class AddExperienceFlow extends StatefulWidget { class _AddExperienceFlowState extends State { static const _minimumInformationUnits = 16.0; static const _nearbyPlaceRadiusMeters = 50; + static const _nearbyPlacesTimeout = Duration(seconds: 12); static const _voicePromptHints = [ 'атмосфера', 'еда', @@ -2445,10 +2402,13 @@ class _AddExperienceFlowState extends State { return const []; } - return context.read().fetchNearbyPlaces( - coordinate: coordinate, - radiusMeters: _nearbyPlaceRadiusMeters, - ); + return context + .read() + .fetchNearbyPlaces( + coordinate: coordinate, + radiusMeters: _nearbyPlaceRadiusMeters, + ) + .timeout(_nearbyPlacesTimeout); } Future _startRecording() async { @@ -2559,8 +2519,14 @@ class _AddExperienceFlowState extends State { ), 1 => _PlaceStep( placesFuture: _nearbyPlacesFuture, + hasCoordinate: widget.coordinate != null, radiusMeters: _nearbyPlaceRadiusMeters, isSubmitting: _submitting, + onRetry: () { + setState(() { + _nearbyPlacesFuture = _loadNearbyPlaces(); + }); + }, onSelect: (place) async { controller.setReviewPlace(place.name); setState(() { @@ -2659,14 +2625,18 @@ class _AddExperienceFlowState extends State { class _PlaceStep extends StatelessWidget { const _PlaceStep({ required this.placesFuture, + required this.hasCoordinate, required this.radiusMeters, required this.isSubmitting, + required this.onRetry, required this.onSelect, }); final Future>? placesFuture; + final bool hasCoordinate; final int radiusMeters; final bool isSubmitting; + final VoidCallback onRetry; final Future Function(PlaceRecommendation) onSelect; @override @@ -2687,59 +2657,60 @@ class _PlaceStep extends StatelessWidget { ), const SizedBox(height: 16), Expanded( - child: FutureBuilder>( - future: placesFuture, - builder: (context, snapshot) { - if (snapshot.connectionState != ConnectionState.done) { - return Center( - child: CircularProgressIndicator(color: tokens.voiceAccent), - ); - } - if (snapshot.hasError) { - return Center( - child: Icon( - Icons.error_outline, - color: tokens.onDark, - size: 42, - ), - ); - } + child: placesFuture == null + ? _PlaceUnavailable( + icon: Icons.location_off_outlined, + message: 'Нет геопозиции', + tokens: tokens, + ) + : FutureBuilder>( + future: placesFuture, + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return Center( + child: CircularProgressIndicator( + color: tokens.voiceAccent, + ), + ); + } + if (snapshot.hasError) { + return _PlaceUnavailable( + icon: Icons.error_outline, + message: 'Места не загрузились', + tokens: tokens, + action: TextButton( + onPressed: onRetry, + child: const Text('Повторить'), + ), + ); + } - final places = snapshot.data ?? const []; - if (places.isEmpty) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.location_off_outlined, - color: tokens.onDark, - size: 42, - ), - const SizedBox(height: 10), - Text( - 'Нет мест в $radiusMetersм', - style: TextStyle(color: tokens.onDark), - ), - ], - ), - ); - } + final places = + snapshot.data ?? const []; + if (places.isEmpty) { + return _PlaceUnavailable( + icon: Icons.location_off_outlined, + message: hasCoordinate + ? 'Нет мест в $radiusMetersм' + : 'Нет геопозиции', + tokens: tokens, + ); + } - return ListView.separated( - itemCount: places.length, - separatorBuilder: (_, _) => const SizedBox(height: 10), - itemBuilder: (context, index) { - final place = places[index]; - return _NearbyPlaceCard( - place: place, - disabled: isSubmitting, - onTap: () => onSelect(place), - ); - }, - ); - }, - ), + return ListView.separated( + itemCount: places.length, + separatorBuilder: (_, _) => const SizedBox(height: 10), + itemBuilder: (context, index) { + final place = places[index]; + return _NearbyPlaceCard( + place: place, + disabled: isSubmitting, + onTap: () => onSelect(place), + ); + }, + ); + }, + ), ), ], ), @@ -2747,6 +2718,35 @@ class _PlaceStep extends StatelessWidget { } } +class _PlaceUnavailable extends StatelessWidget { + const _PlaceUnavailable({ + required this.icon, + required this.message, + required this.tokens, + this.action, + }); + + final IconData icon; + final String message; + final MapflowThemeTokens tokens; + final Widget? action; + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, color: tokens.onDark, size: 42), + const SizedBox(height: 10), + Text(message, style: TextStyle(color: tokens.onDark)), + if (action != null) ...[const SizedBox(height: 8), action!], + ], + ), + ); + } +} + class _FavoriteStep extends StatelessWidget { const _FavoriteStep({ required this.place,