bug fixes
This commit is contained in:
@@ -10,6 +10,6 @@ class AppSettingsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool getMapSettingsFlag(String key) {
|
bool getMapSettingsFlag(String key) {
|
||||||
return sharedPreferences.getBool(key) ?? false;
|
return sharedPreferences.getBool(key) ?? true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ class MapSettings {
|
|||||||
final bool restricted_driving_zones;
|
final bool restricted_driving_zones;
|
||||||
|
|
||||||
MapSettings({
|
MapSettings({
|
||||||
required this.all_placemarks,
|
this.all_placemarks = true,
|
||||||
required this.all_zones,
|
this.all_zones = true,
|
||||||
required this.parking_zones,
|
this.parking_zones = true,
|
||||||
required this.restricted_parking_zones,
|
this.restricted_parking_zones = true,
|
||||||
required this.restricted_driving_zones,
|
this.restricted_driving_zones = true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ class MapSettingsSheet extends StatelessWidget {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<MapSettingsModalBloc>().add(ApllyButtonClick());
|
context.read<MapSettingsModalBloc>().add(ApllyButtonClick());
|
||||||
context.read<MapBloc>().add(UpdateMap());
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import 'dart:ui';
|
|||||||
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 '../../../di/service_locator.dart';
|
import '../../../di/service_locator.dart';
|
||||||
|
import '../../event/map_event.dart';
|
||||||
import '../../event/reserved_ride_event.dart';
|
import '../../event/reserved_ride_event.dart';
|
||||||
import '../../state/reserved_ride_state.dart';
|
import '../../state/reserved_ride_state.dart';
|
||||||
|
import '../../viewmodel/map_bloc.dart';
|
||||||
import '../../viewmodel/reserved_ride_bloc.dart';
|
import '../../viewmodel/reserved_ride_bloc.dart';
|
||||||
import '../dialog/cancel_booking_dialog.dart';
|
import '../dialog/cancel_booking_dialog.dart';
|
||||||
import '../gradient_button.dart';
|
import '../gradient_button.dart';
|
||||||
@@ -125,7 +127,6 @@ class _ReservedRideSheetState extends State<ReservedRideSheet> {
|
|||||||
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// ТАЙМЕР + ИНФО О САМОКАТЕ (КОМПАКТНЫЙ)
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -227,6 +228,7 @@ class _ReservedRideSheetState extends State<ReservedRideSheet> {
|
|||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
if (state.rideStarted) {
|
if (state.rideStarted) {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
context.read<MapBloc>().add(FetchActiveOrders());
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ import '../../../domain/usecase/add_payment_card_usecase.dart';
|
|||||||
import '../../../domain/usecase/get_payment_cards_usecase.dart';
|
import '../../../domain/usecase/get_payment_cards_usecase.dart';
|
||||||
import '../../../domain/usecase/get_profile_usecase.dart';
|
import '../../../domain/usecase/get_profile_usecase.dart';
|
||||||
import '../../../domain/usecase/set_main_payment_card_usecase.dart';
|
import '../../../domain/usecase/set_main_payment_card_usecase.dart';
|
||||||
|
import '../../event/map_event.dart';
|
||||||
import '../../event/payment_method_sheet_event.dart';
|
import '../../event/payment_method_sheet_event.dart';
|
||||||
import '../../event/tariff_sheet_event.dart';
|
import '../../event/tariff_sheet_event.dart';
|
||||||
import '../../state/tariff_sheet_state.dart';
|
import '../../state/tariff_sheet_state.dart';
|
||||||
|
import '../../viewmodel/map_bloc.dart';
|
||||||
import '../../viewmodel/tariff_sheet_bloc.dart';
|
import '../../viewmodel/tariff_sheet_bloc.dart';
|
||||||
import '../gradient_button.dart';
|
import '../gradient_button.dart';
|
||||||
import '../scooter/mini_battery_indicator.dart';
|
import '../scooter/mini_battery_indicator.dart';
|
||||||
@@ -62,6 +64,14 @@ class _TariffSheetState extends State<TariffSheet> {
|
|||||||
}
|
}
|
||||||
return BlocConsumer<TariffSheetBloc, TariffSheetState>(
|
return BlocConsumer<TariffSheetBloc, TariffSheetState>(
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
|
if (state.status == TariffSheetStatus.success &&
|
||||||
|
state.tariffs.isNotEmpty &&
|
||||||
|
_selectedTariffIndex == null) {
|
||||||
|
setState(() {
|
||||||
|
_selectedTariffIndex = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (state.status == TariffSheetStatus.failure && state.errorMessage != null) {
|
if (state.status == TariffSheetStatus.failure && state.errorMessage != null) {
|
||||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@@ -82,6 +92,8 @@ class _TariffSheetState extends State<TariffSheet> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
final selectedIndex = _selectedTariffIndex ?? (state.tariffs.isNotEmpty ? 0 : null);
|
||||||
|
|
||||||
if (state.status == TariffSheetStatus.loading) {
|
if (state.status == TariffSheetStatus.loading) {
|
||||||
return Align(
|
return Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
@@ -283,7 +295,7 @@ class _TariffSheetState extends State<TariffSheet> {
|
|||||||
'Далее ${tariff.drivePrice.toStringAsFixed(2)} ${tariff.currency}/мин.',
|
'Далее ${tariff.drivePrice.toStringAsFixed(2)} ${tariff.currency}/мин.',
|
||||||
'Минута на паузе ${tariff.pausePrice.toStringAsFixed(2)} ${tariff.currency}',
|
'Минута на паузе ${tariff.pausePrice.toStringAsFixed(2)} ${tariff.currency}',
|
||||||
],
|
],
|
||||||
isSelected: _selectedTariffIndex == index,
|
isSelected: selectedIndex == index,
|
||||||
isInsurance: state.isInsurance,
|
isInsurance: state.isInsurance,
|
||||||
tariff: tariff,
|
tariff: tariff,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -412,19 +424,20 @@ class _TariffSheetState extends State<TariffSheet> {
|
|||||||
height: 56,
|
height: 56,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
enabled: _selectedTariffIndex != null && (state.selectedCard != null || state.useBalance),
|
enabled: selectedIndex != null && (state.selectedCard != null || state.useBalance),
|
||||||
onTap: (_selectedTariffIndex != null && (state.selectedCard != null || state.useBalance))
|
onTap: (selectedIndex != null && (state.selectedCard != null || state.useBalance))
|
||||||
? () {
|
? () {
|
||||||
context.read<TariffSheetBloc>().add(
|
context.read<TariffSheetBloc>().add(
|
||||||
BookScooterPressed(
|
BookScooterPressed(
|
||||||
widget.scooter.id,
|
widget.scooter.id,
|
||||||
state.tariffs[_selectedTariffIndex!].id,
|
state.tariffs[selectedIndex].id,
|
||||||
state.tariffs[_selectedTariffIndex!].subscriptionId,
|
state.tariffs[selectedIndex].subscriptionId,
|
||||||
state.useBalance ? null : state.selectedCard?.id,
|
state.useBalance ? null : state.selectedCard?.id,
|
||||||
state.useBalance,
|
state.useBalance,
|
||||||
state.isInsurance
|
state.isInsurance
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
context.read<MapBloc>().add(FetchActiveOrders());
|
||||||
context.pushReplacement('/home/current-rides-sheet');
|
context.pushReplacement('/home/current-rides-sheet');
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class FocusOnScooter extends ScooterEvent {
|
|||||||
class ClearMapPlacemarks extends ScooterEvent {}
|
class ClearMapPlacemarks extends ScooterEvent {}
|
||||||
class ClearMapFocus extends ScooterEvent {}
|
class ClearMapFocus extends ScooterEvent {}
|
||||||
|
|
||||||
|
class FetchActiveOrders extends ScooterEvent {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
BlocConsumer<MapBloc, ScooterState>(
|
BlocConsumer<MapBloc, ScooterState>(
|
||||||
listenWhen: (previous, current) {
|
listenWhen: (previous, current) {
|
||||||
return current.lastNotification !=
|
return current.lastNotification !=
|
||||||
previous.lastNotification ||
|
previous.lastNotification ||
|
||||||
current.flags != previous.flags ||
|
current.flags != previous.flags ||
|
||||||
previous.selectedScooterForFocus?.id
|
previous.selectedScooterForFocus?.id !=
|
||||||
!= current.selectedScooterForFocus?.id;
|
current.selectedScooterForFocus?.id;
|
||||||
},
|
},
|
||||||
|
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
@@ -176,7 +176,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
|
|
||||||
final reservedScooters = _buildScooterPlacemarks(
|
final reservedScooters = _buildScooterPlacemarks(
|
||||||
scooters: state.reservedScooters ?? [],
|
scooters: state.reservedScooters ?? [],
|
||||||
iconAsset: 'assets/icons/scooter_reserved_placemark_fill.png',
|
iconAsset:
|
||||||
|
'assets/icons/scooter_reserved_placemark_fill.png',
|
||||||
isClickable: false,
|
isClickable: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -193,10 +194,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
},
|
},
|
||||||
onCameraPositionChanged:
|
onCameraPositionChanged:
|
||||||
(cameraPosition, reason, finished) {
|
(cameraPosition, reason, finished) {
|
||||||
if (finished) {
|
if (finished) {
|
||||||
_fetchScooters();
|
_fetchScooters();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mapObjects: [
|
mapObjects: [
|
||||||
...zonePolygons,
|
...zonePolygons,
|
||||||
...reservedScooters,
|
...reservedScooters,
|
||||||
@@ -210,7 +211,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
final clusteredPlacemarks = cluster.placemarks;
|
final clusteredPlacemarks = cluster.placemarks;
|
||||||
final filtered = state.scooters.where((scooter) {
|
final filtered = state.scooters.where((scooter) {
|
||||||
return clusteredPlacemarks.any(
|
return clusteredPlacemarks.any(
|
||||||
(pm) => pm.mapId.value == scooter.id.toString(),
|
(pm) => pm.mapId.value == scooter.id.toString(),
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
_onMarkerTap(filtered);
|
_onMarkerTap(filtered);
|
||||||
@@ -241,7 +242,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
|
|
||||||
BlocBuilder<MapBloc, ScooterState>(
|
BlocBuilder<MapBloc, ScooterState>(
|
||||||
buildWhen: (previous, current) =>
|
buildWhen: (previous, current) =>
|
||||||
previous.status != current.status,
|
previous.status != current.status,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.status == ScooterStatus.loading) {
|
if (state.status == ScooterStatus.loading) {
|
||||||
return const Positioned(
|
return const Positioned(
|
||||||
@@ -368,6 +369,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _moveCameraToPoint(
|
Future<void> _moveCameraToPoint(
|
||||||
double lat,
|
double lat,
|
||||||
double lon, {
|
double lon, {
|
||||||
@@ -401,17 +403,13 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
}
|
}
|
||||||
if (flags.hasUnpaidOrder) {
|
if (flags.hasUnpaidOrder) {
|
||||||
_showExistingNotification(
|
_showExistingNotification(
|
||||||
child: UnpaidOrderNotificationCard(
|
child: UnpaidOrderNotificationCard(onClose: () => BotToast.cleanAll()),
|
||||||
onClose: () => BotToast.cleanAll(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (flags.hasFine) {
|
if (flags.hasFine) {
|
||||||
_showExistingNotification(
|
_showExistingNotification(
|
||||||
child: FineNotificationCard(
|
child: FineNotificationCard(onClose: () => BotToast.cleanAll()),
|
||||||
onClose: () => BotToast.cleanAll(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -567,7 +565,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
}) {
|
}) {
|
||||||
return scooters.map((scooter) {
|
return scooters.map((scooter) {
|
||||||
return PlacemarkMapObject(
|
return PlacemarkMapObject(
|
||||||
mapId: MapObjectId('${isClickable ? "" : "reserved_"}${scooter.id}'), // уникальный ID для карты
|
mapId: MapObjectId('${isClickable ? "" : "reserved_"}${scooter.id}'),
|
||||||
|
// уникальный ID для карты
|
||||||
point: Point(latitude: scooter.longitude, longitude: scooter.latitude),
|
point: Point(latitude: scooter.longitude, longitude: scooter.latitude),
|
||||||
icon: PlacemarkIcon.single(
|
icon: PlacemarkIcon.single(
|
||||||
PlacemarkIconStyle(
|
PlacemarkIconStyle(
|
||||||
@@ -595,7 +594,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
for (var polygonData in zone.geometry) {
|
for (var polygonData in zone.geometry) {
|
||||||
if (polygonData.isEmpty) continue;
|
if (polygonData.isEmpty) continue;
|
||||||
|
|
||||||
var outerPoints = polygonData[0].map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList();
|
var outerPoints = polygonData[0]
|
||||||
|
.map((p) => Point(latitude: p.latitude, longitude: p.longitude))
|
||||||
|
.toList();
|
||||||
|
|
||||||
var cleanPoints = <Point>[];
|
var cleanPoints = <Point>[];
|
||||||
for (var p in outerPoints) {
|
for (var p in outerPoints) {
|
||||||
@@ -619,12 +620,14 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
PolygonMapObject(
|
PolygonMapObject(
|
||||||
mapId: const MapObjectId('global_inverse_mask'),
|
mapId: const MapObjectId('global_inverse_mask'),
|
||||||
polygon: Polygon(
|
polygon: Polygon(
|
||||||
outerRing: const LinearRing(points: [
|
outerRing: const LinearRing(
|
||||||
Point(latitude: 85, longitude: -179.9),
|
points: [
|
||||||
Point(latitude: 85, longitude: 179.9),
|
Point(latitude: 85, longitude: -179.9),
|
||||||
Point(latitude: -85, longitude: 179.9),
|
Point(latitude: 85, longitude: 179.9),
|
||||||
Point(latitude: -85, longitude: -179.9),
|
Point(latitude: -85, longitude: 179.9),
|
||||||
]),
|
Point(latitude: -85, longitude: -179.9),
|
||||||
|
],
|
||||||
|
),
|
||||||
innerRings: driveOuterRings, // Вырезаем только зоны Drive
|
innerRings: driveOuterRings, // Вырезаем только зоны Drive
|
||||||
),
|
),
|
||||||
strokeWidth: 0,
|
strokeWidth: 0,
|
||||||
@@ -641,13 +644,17 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
// Стилизация в зависимости от типа зоны
|
// Стилизация в зависимости от типа зоны
|
||||||
if (zone.type == "Drive") {
|
if (zone.type == "Drive") {
|
||||||
borderColor = const Color(0xFF5ECD4C); // Зеленый бордюр
|
borderColor = const Color(0xFF5ECD4C); // Зеленый бордюр
|
||||||
fillColor = Colors.transparent; // Прозрачная внутри маски
|
fillColor = Colors.transparent; // Прозрачная внутри маски
|
||||||
} else if (zone.type == "NotDrive") {
|
} else if (zone.type == "NotDrive") {
|
||||||
borderColor = const Color(0xFFEF4444); // Красный бордюр
|
borderColor = const Color(0xFFEF4444); // Красный бордюр
|
||||||
fillColor = const Color(0xFFEF4444).withOpacity(0.3); // Красная полупрозрачная заливка для наглядности внутри Drive
|
fillColor = const Color(0xFFEF4444).withOpacity(
|
||||||
|
0.3,
|
||||||
|
); // Красная полупрозрачная заливка для наглядности внутри Drive
|
||||||
} else {
|
} else {
|
||||||
borderColor = const Color(0xFFA78BFA); // Фиолетовый бордюр (Парковка)
|
borderColor = const Color(0xFFA78BFA); // Фиолетовый бордюр (Парковка)
|
||||||
fillColor = const Color(0xFFA78BFA).withOpacity(0.25); // Фиолетовая заливка
|
fillColor = const Color(
|
||||||
|
0xFFA78BFA,
|
||||||
|
).withOpacity(0.25); // Фиолетовая заливка
|
||||||
}
|
}
|
||||||
|
|
||||||
int polyIndex = 0;
|
int polyIndex = 0;
|
||||||
@@ -655,7 +662,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
if (polygonData.isEmpty) continue;
|
if (polygonData.isEmpty) continue;
|
||||||
|
|
||||||
// Строим внешнее кольцо текущего полигона
|
// Строим внешнее кольцо текущего полигона
|
||||||
var outerPoints = polygonData[0].map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList();
|
var outerPoints = polygonData[0]
|
||||||
|
.map((p) => Point(latitude: p.latitude, longitude: p.longitude))
|
||||||
|
.toList();
|
||||||
if (outerPoints.first != outerPoints.last) {
|
if (outerPoints.first != outerPoints.last) {
|
||||||
outerPoints.add(outerPoints.first);
|
outerPoints.add(outerPoints.first);
|
||||||
}
|
}
|
||||||
@@ -664,7 +673,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
// Строим внутренние кольца (дыры), если они есть у этого полигона
|
// Строим внутренние кольца (дыры), если они есть у этого полигона
|
||||||
List<LinearRing> innerRings = [];
|
List<LinearRing> innerRings = [];
|
||||||
for (int i = 1; i < polygonData.length; i++) {
|
for (int i = 1; i < polygonData.length; i++) {
|
||||||
var innerPoints = polygonData[i].map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList();
|
var innerPoints = polygonData[i]
|
||||||
|
.map((p) => Point(latitude: p.latitude, longitude: p.longitude))
|
||||||
|
.toList();
|
||||||
if (innerPoints.isNotEmpty) {
|
if (innerPoints.isNotEmpty) {
|
||||||
if (innerPoints.first != innerPoints.last) {
|
if (innerPoints.first != innerPoints.last) {
|
||||||
innerPoints.add(innerPoints.first);
|
innerPoints.add(innerPoints.first);
|
||||||
@@ -677,10 +688,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
objects.add(
|
objects.add(
|
||||||
PolygonMapObject(
|
PolygonMapObject(
|
||||||
mapId: MapObjectId('zone_poly_${zone.type}_${zone.id}_$polyIndex'),
|
mapId: MapObjectId('zone_poly_${zone.type}_${zone.id}_$polyIndex'),
|
||||||
polygon: Polygon(
|
polygon: Polygon(outerRing: outerRing, innerRings: innerRings),
|
||||||
outerRing: outerRing,
|
|
||||||
innerRings: innerRings,
|
|
||||||
),
|
|
||||||
strokeColor: borderColor,
|
strokeColor: borderColor,
|
||||||
strokeWidth: 2.0,
|
strokeWidth: 2.0,
|
||||||
fillColor: fillColor,
|
fillColor: fillColor,
|
||||||
@@ -707,7 +715,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<MapBloc>().add(FetchProfileData());
|
context.read<MapBloc>().add(FetchProfileData());
|
||||||
Scaffold.of(innerContext).openDrawer();
|
Scaffold.of(innerContext).openDrawer();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -721,10 +729,21 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
onPressed: () => context.push("/home/notifications"),
|
onPressed: () => context.push("/home/notifications"),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_RoundButton(
|
BlocBuilder<MapBloc, ScooterState>(
|
||||||
imagePath: 'assets/icons/scooter_placemark.png',
|
buildWhen: (previous, current) =>
|
||||||
onPressed: () => context.push("/home/current-rides-sheet"),
|
previous.activeRidesCount != current.activeRidesCount,
|
||||||
)
|
builder: (context, state) {
|
||||||
|
return _RoundButton(
|
||||||
|
imagePath: 'assets/icons/scooter_placemark.png',
|
||||||
|
count: state.activeRidesCount ?? 0,
|
||||||
|
onPressed: () {
|
||||||
|
context.push("/home/current-rides-sheet").then((_) {
|
||||||
|
context.read<MapBloc>().add(FetchActiveOrders());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -765,9 +784,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
}
|
}
|
||||||
if (flags.hasFine) {
|
if (flags.hasFine) {
|
||||||
_showExistingNotification(
|
_showExistingNotification(
|
||||||
child: FineNotificationCard(
|
child: FineNotificationCard(onClose: () => BotToast.cleanAll()),
|
||||||
onClose: () => BotToast.cleanAll(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -870,32 +887,63 @@ class _RoundIconButton extends StatelessWidget {
|
|||||||
class _RoundButton extends StatelessWidget {
|
class _RoundButton extends StatelessWidget {
|
||||||
final String imagePath;
|
final String imagePath;
|
||||||
final VoidCallback onPressed;
|
final VoidCallback onPressed;
|
||||||
|
final int count;
|
||||||
|
|
||||||
const _RoundButton({
|
const _RoundButton({
|
||||||
required this.imagePath,
|
required this.imagePath,
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
|
this.count = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return GestureDetector(
|
||||||
width: 40,
|
onTap: onPressed,
|
||||||
height: 40,
|
child: Stack(
|
||||||
decoration: BoxDecoration(
|
clipBehavior: Clip.none,
|
||||||
color: AppColors.darkBlue,
|
children: [
|
||||||
borderRadius: BorderRadius.circular(12),
|
Container(
|
||||||
),
|
width: 40,
|
||||||
child: GestureDetector(
|
height: 40,
|
||||||
onTap: onPressed,
|
decoration: BoxDecoration(
|
||||||
child: Center(
|
color: AppColors.darkBlue,
|
||||||
child: Image.asset(
|
borderRadius: BorderRadius.circular(12),
|
||||||
imagePath,
|
),
|
||||||
width: 20,
|
child: Center(
|
||||||
height: 20,
|
child: Image.asset(
|
||||||
fit: BoxFit.contain,
|
imagePath,
|
||||||
color: Colors.white,
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
if (count > 0)
|
||||||
|
Positioned(
|
||||||
|
top: -4,
|
||||||
|
right: -4,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.redAccent,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(minWidth: 18, minHeight: 18),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'$count',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -929,10 +977,12 @@ void _showExistingNotification({required Widget child}) {
|
|||||||
duration: null, // не исчезает, пока пользователь не закроет
|
duration: null, // не исчезает, пока пользователь не закроет
|
||||||
toastBuilder: (_) {
|
toastBuilder: (_) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(top: 120), // тот же отступ, что в listener
|
margin: const EdgeInsets.only(top: 120),
|
||||||
|
// тот же отступ, что в listener
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: child, // PaymentNotificationCard / UnpaidOrderNotificationCard / FineNotificationCard
|
child:
|
||||||
|
child, // PaymentNotificationCard / UnpaidOrderNotificationCard / FineNotificationCard
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ class SupportScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const Divider(height: 1, color: Colors.white24),
|
const Divider(height: 1, color: Colors.white24),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
LinkRow(
|
/*LinkRow(
|
||||||
icon: 'assets/icons/whatsapp.png',
|
icon: 'assets/icons/whatsapp.png',
|
||||||
title: 'WhatsApp',
|
title: 'WhatsApp',
|
||||||
onTap: () => openLink('https://wa.me/375257110909'),
|
onTap: () => openLink('https://wa.me/375257110909'),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: Colors.white24),
|
const Divider(height: 1, color: Colors.white24),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),*/
|
||||||
LinkRow(
|
LinkRow(
|
||||||
icon: 'assets/icons/viber.png',
|
icon: 'assets/icons/viber.png',
|
||||||
title: 'Viber',
|
title: 'Viber',
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ class MapSettingsModalState {
|
|||||||
final bool isRestrictedParkingZoneActive;
|
final bool isRestrictedParkingZoneActive;
|
||||||
|
|
||||||
MapSettingsModalState({
|
MapSettingsModalState({
|
||||||
required this.isAllGeomarksActive,
|
this.isAllGeomarksActive = true,
|
||||||
required this.isAllGeozonesActive,
|
this.isAllGeozonesActive = true,
|
||||||
required this.isRestrictedDrivingZoneActive,
|
this.isRestrictedDrivingZoneActive = true,
|
||||||
required this.isParkingZoneActive,
|
this.isParkingZoneActive = true,
|
||||||
required this.isRestrictedParkingZoneActive,
|
this.isRestrictedParkingZoneActive = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
MapSettingsModalState copyWith({
|
MapSettingsModalState copyWith({
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class ScooterState {
|
|||||||
final double balance;
|
final double balance;
|
||||||
final UserCheckFlags flags;
|
final UserCheckFlags flags;
|
||||||
final ClientNotification? lastNotification;
|
final ClientNotification? lastNotification;
|
||||||
|
final int activeRidesCount;
|
||||||
|
|
||||||
ScooterState({
|
ScooterState({
|
||||||
this.scooters = const [],
|
this.scooters = const [],
|
||||||
@@ -38,6 +39,7 @@ class ScooterState {
|
|||||||
this.balance = 999,
|
this.balance = 999,
|
||||||
this.flags = const UserCheckFlags(hasFine: false, hasUnpaidOrder: false, hasCard: false),
|
this.flags = const UserCheckFlags(hasFine: false, hasUnpaidOrder: false, hasCard: false),
|
||||||
this.lastNotification,
|
this.lastNotification,
|
||||||
|
this.activeRidesCount = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
ScooterState copyWith({
|
ScooterState copyWith({
|
||||||
@@ -55,6 +57,7 @@ class ScooterState {
|
|||||||
double? balance,
|
double? balance,
|
||||||
UserCheckFlags? flags,
|
UserCheckFlags? flags,
|
||||||
ClientNotification? lastNotification,
|
ClientNotification? lastNotification,
|
||||||
|
int? activeRidesCount,
|
||||||
}) {
|
}) {
|
||||||
return ScooterState(
|
return ScooterState(
|
||||||
scooters: scooters ?? this.scooters,
|
scooters: scooters ?? this.scooters,
|
||||||
@@ -71,6 +74,7 @@ class ScooterState {
|
|||||||
balance: balance ?? this.balance,
|
balance: balance ?? this.balance,
|
||||||
flags: flags ?? this.flags,
|
flags: flags ?? this.flags,
|
||||||
lastNotification: lastNotification ?? this.lastNotification,
|
lastNotification: lastNotification ?? this.lastNotification,
|
||||||
|
activeRidesCount: activeRidesCount ?? this.activeRidesCount,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class TariffSheetState {
|
|||||||
this.status = TariffSheetStatus.initial,
|
this.status = TariffSheetStatus.initial,
|
||||||
this.tariffs = const [],
|
this.tariffs = const [],
|
||||||
this.selectedCard,
|
this.selectedCard,
|
||||||
this.isInsurance = false,
|
this.isInsurance = true,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
this.userBalance = 0,
|
this.userBalance = 0,
|
||||||
this.useBalance = false,
|
this.useBalance = false,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class MapBloc extends Bloc<ScooterEvent, ScooterState> {
|
|||||||
final LogoutUseCase logoutUseCase;
|
final LogoutUseCase logoutUseCase;
|
||||||
final SplashBloc splashBloc;
|
final SplashBloc splashBloc;
|
||||||
StreamSubscription<ClientNotification>? _notificationSubscription;
|
StreamSubscription<ClientNotification>? _notificationSubscription;
|
||||||
|
Timer? _ordersTimer;
|
||||||
|
|
||||||
Future<List<Zone>>? _zonesFuture;
|
Future<List<Zone>>? _zonesFuture;
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ class MapBloc extends Bloc<ScooterEvent, ScooterState> {
|
|||||||
on<FocusOnScooter>(_onFocusOnScooter);
|
on<FocusOnScooter>(_onFocusOnScooter);
|
||||||
on<ClearMapPlacemarks>(_onClearMapPlacemarks);
|
on<ClearMapPlacemarks>(_onClearMapPlacemarks);
|
||||||
on<ClearMapFocus>(_onClearMapFocus);
|
on<ClearMapFocus>(_onClearMapFocus);
|
||||||
|
on<FetchActiveOrders>(_onFetchActiveOrders);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Zone>> _getZones() async {
|
Future<List<Zone>> _getZones() async {
|
||||||
@@ -344,4 +346,50 @@ class MapBloc extends Bloc<ScooterEvent, ScooterState> {
|
|||||||
selectedScooterForFocus: null,
|
selectedScooterForFocus: null,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _startOrdersTimer() {
|
||||||
|
_ordersTimer?.cancel();
|
||||||
|
// Опрашиваем раз в 15 секунд, если есть активные поездки
|
||||||
|
_ordersTimer = Timer.periodic(const Duration(seconds: 15), (_) {
|
||||||
|
add(FetchActiveOrders());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _stopOrdersTimer() {
|
||||||
|
_ordersTimer?.cancel();
|
||||||
|
_ordersTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFetchActiveOrders(
|
||||||
|
FetchActiveOrders event,
|
||||||
|
Emitter<ScooterState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final result = await getClientOrdersUsecase();
|
||||||
|
if (result is Success<List<ScooterOrder>>) {
|
||||||
|
final activeOrders = result.data ?? [];
|
||||||
|
final count = activeOrders.length;
|
||||||
|
|
||||||
|
// Управляем таймером в зависимости от наличия активных поездок
|
||||||
|
if (count > 0 && (_ordersTimer == null || !_ordersTimer!.isActive)) {
|
||||||
|
_startOrdersTimer();
|
||||||
|
} else if (count == 0) {
|
||||||
|
_stopOrdersTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(state.copyWith(
|
||||||
|
activeRidesCount: count,
|
||||||
|
reservedScooters: activeOrders.map((e) => e.scooter).toList(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("Error fetching active orders: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_stopOrdersTimer();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,13 +15,7 @@ class MapSettingsModalBloc extends Bloc<MapSettingsModalEvent, MapSettingsModalS
|
|||||||
final SaveMapSettingsUsecase saveMapSettingsUsecase;
|
final SaveMapSettingsUsecase saveMapSettingsUsecase;
|
||||||
|
|
||||||
MapSettingsModalBloc(this.getMapSettingsUsecase, this.saveMapSettingsUsecase)
|
MapSettingsModalBloc(this.getMapSettingsUsecase, this.saveMapSettingsUsecase)
|
||||||
: super(MapSettingsModalState(
|
: super(MapSettingsModalState()) {
|
||||||
isAllGeomarksActive: true,
|
|
||||||
isAllGeozonesActive: true,
|
|
||||||
isParkingZoneActive: true,
|
|
||||||
isRestrictedParkingZoneActive: true,
|
|
||||||
isRestrictedDrivingZoneActive: true,
|
|
||||||
)) {
|
|
||||||
on<AllGeozonesToggled>(_onAllGeozonesToggled);
|
on<AllGeozonesToggled>(_onAllGeozonesToggled);
|
||||||
on<AllGeomarksToggled>(_onAllGeomarksToggled);
|
on<AllGeomarksToggled>(_onAllGeomarksToggled);
|
||||||
on<ParkingZonesToggled>(_onParkingZonesToggled);
|
on<ParkingZonesToggled>(_onParkingZonesToggled);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: be_happy
|
|||||||
description: ""
|
description: ""
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.0.3+1
|
version: 1.0.4+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.8.1
|
sdk: ^3.8.1
|
||||||
|
|||||||
Reference in New Issue
Block a user