MENA region data enrichment scripts` -> Wait, let's keep it concise and clean.

*   *Option 3:* `feat: add tourist landmarks support with UI sheets, data models
This commit is contained in:
Hamza-Ayed
2026-09-21 05:21:44 +03:00
parent cff5a8e1ce
commit dc9a736e26
29 changed files with 13396 additions and 1982 deletions
+157 -58
View File
@@ -22,6 +22,11 @@ import 'widgets/add_place_sheet.dart';
import 'widgets/vehicle_customizer_sheet.dart';
import 'widgets/about_awards_sheet.dart';
import 'widgets/cinematic_3d_studio_sheet.dart';
import 'widgets/siro_drawer_widget.dart';
import 'widgets/tourist_landmarks_sheet.dart';
import 'widgets/settings_sheet.dart';
import 'widgets/system_updates_sheet.dart';
import '../common/siro_toast.dart';
class MapView extends StatefulWidget {
const MapView({super.key});
@@ -31,6 +36,7 @@ class MapView extends StatefulWidget {
}
class _MapViewState extends State<MapView> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final TextEditingController _searchController = TextEditingController();
final FocusNode _searchFocusNode = FocusNode();
bool _isSearchFocused = false;
@@ -117,6 +123,36 @@ class _MapViewState extends State<MapView> {
);
}
void _showTouristLandmarks(BuildContext context, NavigationCubit cubit, NavigationState state) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => TouristLandmarksSheet(
cubit: cubit,
state: state,
),
);
}
void _showSettingsSheet(BuildContext context, NavigationCubit cubit) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => SettingsSheet(cubit: cubit),
);
}
void _showSystemUpdatesSheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => const SystemUpdatesSheet(),
);
}
void _showCinematic3DStudio(BuildContext context, NavigationCubit cubit, NavigationState state) {
showModalBottomSheet(
context: context,
@@ -170,14 +206,10 @@ class _MapViewState extends State<MapView> {
position: initialLocation,
);
if (mounted && ok) {
messenger.showSnackBar(
const SnackBar(
content: Text(
'تم إرسال البلاغ بنجاح وتحديث الشبكة التشاركية.',
style: TextStyle(fontSize: 12),
),
backgroundColor: AppColors.tacticalEmerald,
),
_showLuxuryNotification(
context,
message: 'تم إرسال البلاغ بنجاح وتحديث الشبكة التشاركية.',
isError: false,
);
}
},
@@ -196,21 +228,16 @@ class _MapViewState extends State<MapView> {
backgroundColor: Colors.transparent,
builder: (_) => AddPlaceSheet(
onSubmit: (name, cat) async {
final messenger = ScaffoldMessenger.of(context);
final ok = await cubit.submitPlace(
name,
cat,
position: initialLocation,
);
if (mounted) {
messenger.showSnackBar(
SnackBar(
content: Text(
ok ? 'تمت إضافة المكان بنجاح! شكراً لمساهمتك.' : 'تعذر حفظ المكان، يرجى المحاولة لاحقاً.',
style: const TextStyle(fontSize: 12),
),
backgroundColor: ok ? AppColors.tacticalEmerald : AppColors.coralDanger,
),
_showLuxuryNotification(
context,
message: ok ? 'تمت إضافة المكان بنجاح! شكراً لمساهمتك.' : 'تعذر حفظ المكان، يرجى المحاولة لاحقاً.',
isError: !ok,
);
}
},
@@ -222,6 +249,18 @@ class _MapViewState extends State<MapView> {
});
}
void _showLuxuryNotification(
BuildContext context, {
required String message,
bool isError = false,
}) {
if (isError) {
SiroToast.showError(context, message);
} else {
SiroToast.showSuccess(context, message);
}
}
void _showPlaceGatesSheet(BuildContext context, NavigationCubit cubit, PlaceModel place) {
showModalBottomSheet(
context: context,
@@ -255,14 +294,10 @@ class _MapViewState extends State<MapView> {
return BlocConsumer<NavigationCubit, NavigationState>(
listener: (context, state) {
if (state.errorMessage != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
state.errorMessage!,
style: const TextStyle(fontSize: 12),
),
backgroundColor: AppColors.coralDanger,
),
_showLuxuryNotification(
context,
message: state.errorMessage!,
isError: true,
);
}
@@ -313,8 +348,21 @@ class _MapViewState extends State<MapView> {
valueListenable: PipService.instance.isInPipMode,
builder: (context, isInPip, _) {
return Scaffold(
key: _scaffoldKey,
resizeToAvoidBottomInset: false,
backgroundColor: AppColors.canvasLight,
drawer: SiroDrawerWidget(
cubit: cubit,
state: state,
onOpenTouristLandmarks: () => _showTouristLandmarks(context, cubit, state),
onOpen3DStudio: () => _showCinematic3DStudio(context, cubit, state),
onOpenLayers: () => _showLayerSelector(context, cubit, state.mapTheme),
onOpenSettings: () => _showSettingsSheet(context, cubit),
onOpenUpdates: () => _showSystemUpdatesSheet(context),
onOpenAbout: () => _showAboutAwardsSheet(context),
onStartReportHazard: () => cubit.startLocationPicking('hazard'),
onStartAddPlace: () => cubit.startLocationPicking('place'),
),
body: SizedBox.expand(
child: Stack(
fit: StackFit.expand,
@@ -375,7 +423,7 @@ class _MapViewState extends State<MapView> {
_searchController.clear();
cubit.clearSearch();
},
onMenuTap: () => _showLayerSelector(context, cubit, state.mapTheme),
onMenuTap: () => _scaffoldKey.currentState?.openDrawer(),
),
const SizedBox(height: 10),
ExplorePanelWidget(
@@ -383,6 +431,7 @@ class _MapViewState extends State<MapView> {
_searchController.text = q;
cubit.searchImmediately(q);
},
onTouristSelected: () => _showTouristLandmarks(context, cubit, state),
),
],
),
@@ -1276,38 +1325,6 @@ class _MapViewState extends State<MapView> {
onTap: () => _showCinematic3DStudio(context, cubit, state),
),
const SizedBox(height: 12),
// Layer Selector Button
_buildFloatingCircle(
icon: Icons.layers_rounded,
color: AppColors.textPrimary,
tooltip: 'طبقات الخريطة',
onTap: () => _showLayerSelector(context, cubit, state.mapTheme),
),
const SizedBox(height: 12),
// Siro Map Platform Architecture & Info Button
_buildFloatingCircle(
icon: Icons.info_outline_rounded,
color: AppColors.appleBlue,
tooltip: 'عن سيرو ماب والمنظومة التقنية',
onTap: () => _showAboutAwardsSheet(context),
),
const SizedBox(height: 12),
// Add Place Button
_buildFloatingCircle(
icon: Icons.add_location_alt_rounded,
color: AppColors.appleBlue,
tooltip: 'إضافة مكان',
onTap: () => cubit.startLocationPicking('place'),
),
const SizedBox(height: 12),
// Report Hazard Button
_buildFloatingCircle(
icon: Icons.warning_amber_rounded,
color: AppColors.sovereignGold,
tooltip: 'إبلاغ عن حالة طريق',
onTap: () => cubit.startLocationPicking('hazard'),
),
const SizedBox(height: 12),
// Recenter GPS Button
_buildFloatingCircle(
icon: Icons.my_location_rounded,
@@ -1374,6 +1391,88 @@ class _MapViewState extends State<MapView> {
),
),
// ── 6c. FLOATING 3D ORBIT STATUS & STOP BUTTON ──
if (!isInPip && state.isOrbiting)
Positioned(
left: 16,
bottom: (state.speed > 3.0) ? 100 : 32,
child: SafeArea(
top: false,
child: Material(
color: Colors.transparent,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFF1D1D1F).withValues(alpha: 0.94),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: const Color(0xFFEA580C).withValues(alpha: 0.8),
width: 1.5,
),
boxShadow: const [
BoxShadow(
color: Color(0x3D000000),
blurRadius: 16,
offset: Offset(0, 4),
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () => cubit.stopOrbit(),
borderRadius: BorderRadius.circular(12),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.stop_circle_rounded,
color: AppColors.coralDanger,
size: 18,
),
SizedBox(width: 6),
Text(
'إيقاف الدوران 360°',
style: TextStyle(
fontFamily: '.SF Pro Text',
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w700,
),
),
],
),
),
const SizedBox(width: 10),
Container(
width: 1,
height: 16,
color: Colors.white.withValues(alpha: 0.2),
),
const SizedBox(width: 8),
GestureDetector(
onTap: () => _showCinematic3DStudio(context, cubit, state),
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15),
shape: BoxShape.circle,
),
child: const Icon(
Icons.tune_rounded,
color: Colors.white,
size: 14,
),
),
),
],
),
),
),
),
),
// ── 7. LOADING OVERLAY ──
if (state.status == NavigationStatus.loading)
Container(