This commit is contained in:
@@ -73,6 +73,7 @@ class _MapContentState extends State<_MapContent> {
|
|||||||
final selected = state.selectedPlace;
|
final selected = state.selectedPlace;
|
||||||
final userCoordinate = state.userCoordinate;
|
final userCoordinate = state.userCoordinate;
|
||||||
final targetCenter = _focusCenter(state);
|
final targetCenter = _focusCenter(state);
|
||||||
|
final reviewCoordinate = userCoordinate ?? selected?.coordinate;
|
||||||
final placeCubit = context.read<PlaceCubit>();
|
final placeCubit = context.read<PlaceCubit>();
|
||||||
final traitCounts = _countPlaceTraits(state.places);
|
final traitCounts = _countPlaceTraits(state.places);
|
||||||
final availableTraits = [
|
final availableTraits = [
|
||||||
@@ -98,7 +99,6 @@ class _MapContentState extends State<_MapContent> {
|
|||||||
userCoordinate: userCoordinate,
|
userCoordinate: userCoordinate,
|
||||||
onPlaceTap: (place) => _selectAndShowPlace(context, place),
|
onPlaceTap: (place) => _selectAndShowPlace(context, place),
|
||||||
),
|
),
|
||||||
if (targetCenter == null) const _MapTargetLoadingOverlay(),
|
|
||||||
SafeArea(
|
SafeArea(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
@@ -118,10 +118,9 @@ class _MapContentState extends State<_MapContent> {
|
|||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topRight,
|
alignment: Alignment.topRight,
|
||||||
child: _AddReviewAction(
|
child: _AddReviewAction(
|
||||||
onPressed: () => _openAddFlow(
|
onPressed: reviewCoordinate == null
|
||||||
context,
|
? null
|
||||||
userCoordinate ?? selected?.coordinate,
|
: () => _openAddFlow(context, reviewCoordinate),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -614,7 +613,7 @@ class _UserAvatar extends StatelessWidget {
|
|||||||
class _AddReviewAction extends StatelessWidget {
|
class _AddReviewAction extends StatelessWidget {
|
||||||
const _AddReviewAction({required this.onPressed});
|
const _AddReviewAction({required this.onPressed});
|
||||||
|
|
||||||
final VoidCallback onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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 {
|
class _MapError extends StatelessWidget {
|
||||||
const _MapError({required this.message});
|
const _MapError({required this.message});
|
||||||
|
|
||||||
@@ -2385,6 +2341,7 @@ class AddExperienceFlow extends StatefulWidget {
|
|||||||
class _AddExperienceFlowState extends State<AddExperienceFlow> {
|
class _AddExperienceFlowState extends State<AddExperienceFlow> {
|
||||||
static const _minimumInformationUnits = 16.0;
|
static const _minimumInformationUnits = 16.0;
|
||||||
static const _nearbyPlaceRadiusMeters = 50;
|
static const _nearbyPlaceRadiusMeters = 50;
|
||||||
|
static const _nearbyPlacesTimeout = Duration(seconds: 12);
|
||||||
static const _voicePromptHints = [
|
static const _voicePromptHints = [
|
||||||
'атмосфера',
|
'атмосфера',
|
||||||
'еда',
|
'еда',
|
||||||
@@ -2445,10 +2402,13 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
|
|||||||
return const [];
|
return const [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.read<PlacesRepository>().fetchNearbyPlaces(
|
return context
|
||||||
|
.read<PlacesRepository>()
|
||||||
|
.fetchNearbyPlaces(
|
||||||
coordinate: coordinate,
|
coordinate: coordinate,
|
||||||
radiusMeters: _nearbyPlaceRadiusMeters,
|
radiusMeters: _nearbyPlaceRadiusMeters,
|
||||||
);
|
)
|
||||||
|
.timeout(_nearbyPlacesTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _startRecording() async {
|
Future<void> _startRecording() async {
|
||||||
@@ -2559,8 +2519,14 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
|
|||||||
),
|
),
|
||||||
1 => _PlaceStep(
|
1 => _PlaceStep(
|
||||||
placesFuture: _nearbyPlacesFuture,
|
placesFuture: _nearbyPlacesFuture,
|
||||||
|
hasCoordinate: widget.coordinate != null,
|
||||||
radiusMeters: _nearbyPlaceRadiusMeters,
|
radiusMeters: _nearbyPlaceRadiusMeters,
|
||||||
isSubmitting: _submitting,
|
isSubmitting: _submitting,
|
||||||
|
onRetry: () {
|
||||||
|
setState(() {
|
||||||
|
_nearbyPlacesFuture = _loadNearbyPlaces();
|
||||||
|
});
|
||||||
|
},
|
||||||
onSelect: (place) async {
|
onSelect: (place) async {
|
||||||
controller.setReviewPlace(place.name);
|
controller.setReviewPlace(place.name);
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -2659,14 +2625,18 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
|
|||||||
class _PlaceStep extends StatelessWidget {
|
class _PlaceStep extends StatelessWidget {
|
||||||
const _PlaceStep({
|
const _PlaceStep({
|
||||||
required this.placesFuture,
|
required this.placesFuture,
|
||||||
|
required this.hasCoordinate,
|
||||||
required this.radiusMeters,
|
required this.radiusMeters,
|
||||||
required this.isSubmitting,
|
required this.isSubmitting,
|
||||||
|
required this.onRetry,
|
||||||
required this.onSelect,
|
required this.onSelect,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Future<List<PlaceRecommendation>>? placesFuture;
|
final Future<List<PlaceRecommendation>>? placesFuture;
|
||||||
|
final bool hasCoordinate;
|
||||||
final int radiusMeters;
|
final int radiusMeters;
|
||||||
final bool isSubmitting;
|
final bool isSubmitting;
|
||||||
|
final VoidCallback onRetry;
|
||||||
final Future<void> Function(PlaceRecommendation) onSelect;
|
final Future<void> Function(PlaceRecommendation) onSelect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -2687,42 +2657,43 @@ class _PlaceStep extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: FutureBuilder<List<PlaceRecommendation>>(
|
child: placesFuture == null
|
||||||
|
? _PlaceUnavailable(
|
||||||
|
icon: Icons.location_off_outlined,
|
||||||
|
message: 'Нет геопозиции',
|
||||||
|
tokens: tokens,
|
||||||
|
)
|
||||||
|
: FutureBuilder<List<PlaceRecommendation>>(
|
||||||
future: placesFuture,
|
future: placesFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState != ConnectionState.done) {
|
if (snapshot.connectionState != ConnectionState.done) {
|
||||||
return Center(
|
return Center(
|
||||||
child: CircularProgressIndicator(color: tokens.voiceAccent),
|
child: CircularProgressIndicator(
|
||||||
|
color: tokens.voiceAccent,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return Center(
|
return _PlaceUnavailable(
|
||||||
child: Icon(
|
icon: Icons.error_outline,
|
||||||
Icons.error_outline,
|
message: 'Места не загрузились',
|
||||||
color: tokens.onDark,
|
tokens: tokens,
|
||||||
size: 42,
|
action: TextButton(
|
||||||
|
onPressed: onRetry,
|
||||||
|
child: const Text('Повторить'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final places = snapshot.data ?? const <PlaceRecommendation>[];
|
final places =
|
||||||
|
snapshot.data ?? const <PlaceRecommendation>[];
|
||||||
if (places.isEmpty) {
|
if (places.isEmpty) {
|
||||||
return Center(
|
return _PlaceUnavailable(
|
||||||
child: Column(
|
icon: Icons.location_off_outlined,
|
||||||
mainAxisSize: MainAxisSize.min,
|
message: hasCoordinate
|
||||||
children: [
|
? 'Нет мест в $radiusMetersм'
|
||||||
Icon(
|
: 'Нет геопозиции',
|
||||||
Icons.location_off_outlined,
|
tokens: tokens,
|
||||||
color: tokens.onDark,
|
|
||||||
size: 42,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text(
|
|
||||||
'Нет мест в $radiusMetersм',
|
|
||||||
style: TextStyle(color: tokens.onDark),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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 {
|
class _FavoriteStep extends StatelessWidget {
|
||||||
const _FavoriteStep({
|
const _FavoriteStep({
|
||||||
required this.place,
|
required this.place,
|
||||||
|
|||||||
Reference in New Issue
Block a user