feat: استيراد كود سيرو إلى تريبز (سيرو @ecfe7568) — بلا تعديل
قرار المالك 2026-07-27: باك إند سيرو PHP هو المعتمد، وتطبيقاته المجرّبة ميدانياً تحل محل إعادة البناء المؤرشفة. سيرو نفسه لم يُمسّ. الخريطة: backend · payment_server · loction_server · ride_server · passenger_server · docker · dashboard · stress_test → الجذر siro_rider → apps/rider siro_driver → apps/driver siro_admin → dashboards/admin siro_service → dashboards/service android_bot → apps/android_bot socialBot → apps/socialBot نُسخ المتعقَّب في git سيرو فقط عبر `git archive` (3,198 ملفاً / ~169 م.ب) لا `cp -r` — فاستُثنيت مخلفات البناء تلقائياً. بلا أي تعديل محتوى عمداً: كل ما يلي يصير فرقاً مقروءاً مقابل المصدر. لم يُستورد وسببه: siromove.com (الموقع التسويقي يبقى marketing/ في تريبز، سيرو فيه 8 ملفات) · docs و planning (تريبز له docs/ الخاص) · deploy.sh (ليس نشراً على سيرفر بل `git add . && git push origin --all` — فخّ في مستودع آخر) · transit_dashboard (بانتظار قرار مصير backend-transit و dashboards/transit-web). ⚠️ لا يبني بعد — ثلاثة نواقص متوقعة ومقصودة: 1. `.env` و `lib/env/env.g.dart` غير متعقَّبين في سيرو (أسرار لكل مستأجر): كل تطبيق فلاتر يحتاج .env خاصاً ثم توليد env.g.dart بـ build_runner. 2. إعدادات Firebase (9 ملفات google-services.json و GoogleService-Info.plist) يستبعدها .gitignore تريبز — ولكل مستأجر مشروع Firebase خاص أصلاً. 3. apps/driver في سيرو يشير إلى `../../Intaleq/packages/get` خارج المستودع → يجب ضمّ الحزم داخله أسوة بـ apps/rider. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9909d9b4c1
commit
4d8414c96b
@@ -0,0 +1,357 @@
|
||||
import 'package:siro_rider/print.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:timezone/data/latest.dart' as tz;
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
import '../../main.dart';
|
||||
|
||||
class NotificationController extends GetxController {
|
||||
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
FlutterLocalNotificationsPlugin get plugin =>
|
||||
_flutterLocalNotificationsPlugin;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
initNotifications();
|
||||
}
|
||||
|
||||
// Initializes the local notifications plugin
|
||||
Future<void> initNotifications() async {
|
||||
const AndroidInitializationSettings android =
|
||||
AndroidInitializationSettings('@mipmap/launcher_icon');
|
||||
DarwinInitializationSettings ios = DarwinInitializationSettings(
|
||||
requestAlertPermission: true,
|
||||
requestBadgePermission: true,
|
||||
requestSoundPermission: true,
|
||||
// onDidReceiveLocalNotification:
|
||||
// (int id, String? title, String? body, String? payload) async {},
|
||||
);
|
||||
InitializationSettings initializationSettings =
|
||||
InitializationSettings(android: android, iOS: ios);
|
||||
await _flutterLocalNotificationsPlugin.initialize(
|
||||
settings: initializationSettings);
|
||||
|
||||
tz.initializeTimeZones();
|
||||
Log.print('Notifications initialized');
|
||||
}
|
||||
|
||||
// Displays a notification with the given title and message
|
||||
void showNotification(String title, String message, String tone) async {
|
||||
final AndroidNotificationDetails android = AndroidNotificationDetails(
|
||||
'high_importance_channel',
|
||||
'High Importance Notifications',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
showWhen: false,
|
||||
sound: RawResourceAndroidNotificationSound(tone),
|
||||
);
|
||||
|
||||
const DarwinNotificationDetails ios = DarwinNotificationDetails(
|
||||
sound: 'default',
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentSound: true,
|
||||
);
|
||||
|
||||
final NotificationDetails details =
|
||||
NotificationDetails(android: android, iOS: ios);
|
||||
await _flutterLocalNotificationsPlugin.show(
|
||||
id: 0, title: title, body: message, notificationDetails: details);
|
||||
Log.print('Notification shown: $title - $message');
|
||||
}
|
||||
|
||||
// 🆕 Static method for background isolates where GetX might not be initialized
|
||||
static Future<void> showBackgroundNotification(String title, String message, String tone) async {
|
||||
final FlutterLocalNotificationsPlugin plugin = FlutterLocalNotificationsPlugin();
|
||||
const AndroidInitializationSettings android = AndroidInitializationSettings('@mipmap/launcher_icon');
|
||||
const DarwinInitializationSettings ios = DarwinInitializationSettings();
|
||||
await plugin.initialize(settings: const InitializationSettings(android: android, iOS: ios));
|
||||
|
||||
final AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
|
||||
'high_importance_channel',
|
||||
'High Importance Notifications',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
showWhen: false,
|
||||
sound: RawResourceAndroidNotificationSound(tone),
|
||||
);
|
||||
const DarwinNotificationDetails iosDetails = DarwinNotificationDetails(sound: 'default');
|
||||
|
||||
await plugin.show(id: 0, title: title, body: message, notificationDetails: NotificationDetails(android: androidDetails, iOS: iosDetails));
|
||||
Log.print('Background notification shown: $title - $message');
|
||||
}
|
||||
// /Users/hamzaaleghwairyeen/development/App/ride 2/lib/controller/firebase/local_notification.dart
|
||||
|
||||
// Assume _flutterLocalNotificationsPlugin is initialized somewhere in your code
|
||||
|
||||
// void scheduleNotificationsForSevenDays(
|
||||
// String title, String message, String tone) async {
|
||||
// final AndroidNotificationDetails android = AndroidNotificationDetails(
|
||||
// 'high_importance_channel',
|
||||
// 'High Importance Notifications',
|
||||
// importance: Importance.max,
|
||||
// priority: Priority.high,
|
||||
// sound: RawResourceAndroidNotificationSound(tone),
|
||||
// );
|
||||
|
||||
// const DarwinNotificationDetails ios = DarwinNotificationDetails(
|
||||
// sound: 'default',
|
||||
// presentAlert: true,
|
||||
// presentBadge: true,
|
||||
// presentSound: true,
|
||||
// );
|
||||
|
||||
// final NotificationDetails details =
|
||||
// NotificationDetails(android: android, iOS: ios);
|
||||
|
||||
// // Check for the exact alarm permission on Android 12 and above
|
||||
// if (Platform.isAndroid) {
|
||||
// if (await Permission.scheduleExactAlarm.isDenied) {
|
||||
// if (await Permission.scheduleExactAlarm.request().isGranted) {
|
||||
// Log.print('SCHEDULE_EXACT_ALARM permission granted');
|
||||
// } else {
|
||||
// Log.print('SCHEDULE_EXACT_ALARM permission denied');
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Schedule notifications for the next 7 days
|
||||
// for (int day = 0; day < 7; day++) {
|
||||
// // Schedule for 8:00 AM
|
||||
// await _scheduleNotificationForTime(
|
||||
// day, 8, 0, title, message, details, day * 1000 + 1);
|
||||
|
||||
// // Schedule for 3:00 PM
|
||||
// await _scheduleNotificationForTime(
|
||||
// day, 15, 0, title, message, details, day * 1000 + 2); // Unique ID
|
||||
|
||||
// // Schedule for 8:00 PM
|
||||
// await _scheduleNotificationForTime(
|
||||
// day, 20, 0, title, message, details, day * 1000 + 3); // Unique ID
|
||||
// }
|
||||
|
||||
// Log.print('Notifications scheduled successfully for the next 7 days');
|
||||
// }
|
||||
void scheduleNotificationsForSevenDays(
|
||||
String title, String message, String tone) async {
|
||||
final AndroidNotificationDetails android = AndroidNotificationDetails(
|
||||
'high_importance_channel',
|
||||
'High Importance Notifications',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
sound: RawResourceAndroidNotificationSound(tone),
|
||||
);
|
||||
|
||||
const DarwinNotificationDetails ios = DarwinNotificationDetails(
|
||||
sound: 'default',
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentSound: true,
|
||||
);
|
||||
|
||||
final NotificationDetails details =
|
||||
NotificationDetails(android: android, iOS: ios);
|
||||
|
||||
// Check for the exact alarm permission on Android 12 and above
|
||||
if (Platform.isAndroid) {
|
||||
if (await Permission.scheduleExactAlarm.isDenied) {
|
||||
if (await Permission.scheduleExactAlarm.request().isGranted) {
|
||||
Log.print('SCHEDULE_EXACT_ALARM permission granted');
|
||||
} else {
|
||||
Log.print('SCHEDULE_EXACT_ALARM permission denied');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule notifications for the next 7 days
|
||||
for (int day = 0; day < 7; day++) {
|
||||
// List of notification times
|
||||
final notificationTimes = [
|
||||
{'hour': 8, 'minute': 0, 'id': day * 1000 + 1}, // 8:00 AM
|
||||
{'hour': 15, 'minute': 0, 'id': day * 1000 + 2}, // 3:00 PM
|
||||
{'hour': 20, 'minute': 0, 'id': day * 1000 + 3}, // 8:00 PM
|
||||
];
|
||||
|
||||
for (var time in notificationTimes) {
|
||||
final notificationId = time['id'] as int;
|
||||
|
||||
// Check if this notification ID is already stored
|
||||
bool isScheduled = box.read('notification_$notificationId') ?? false;
|
||||
|
||||
if (!isScheduled) {
|
||||
// Schedule the notification if not already scheduled
|
||||
await _scheduleNotificationForTime(
|
||||
day,
|
||||
time['hour'] as int,
|
||||
time['minute'] as int,
|
||||
title,
|
||||
message,
|
||||
details,
|
||||
notificationId,
|
||||
);
|
||||
|
||||
// Mark this notification ID as scheduled in GetStorage
|
||||
box.write('notification_$notificationId', true);
|
||||
} else {
|
||||
Log.print('Notification with ID $notificationId is already scheduled.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.print('Notifications scheduled successfully for the next 7 days');
|
||||
}
|
||||
|
||||
void scheduleNotificationsForTimeSelected(
|
||||
String title, String message, String tone, DateTime timeSelected) async {
|
||||
final AndroidNotificationDetails android = AndroidNotificationDetails(
|
||||
'high_importance_channel',
|
||||
'High Importance Notifications',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
sound: RawResourceAndroidNotificationSound(tone),
|
||||
);
|
||||
|
||||
const DarwinNotificationDetails ios = DarwinNotificationDetails(
|
||||
sound: 'default',
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentSound: true,
|
||||
);
|
||||
|
||||
final NotificationDetails details =
|
||||
NotificationDetails(android: android, iOS: ios);
|
||||
|
||||
// Check for the exact alarm permission on Android 12 and above
|
||||
if (Platform.isAndroid) {
|
||||
if (await Permission.scheduleExactAlarm.isDenied) {
|
||||
if (await Permission.scheduleExactAlarm.request().isGranted) {
|
||||
Log.print('SCHEDULE_EXACT_ALARM permission granted');
|
||||
} else {
|
||||
Log.print('SCHEDULE_EXACT_ALARM permission denied');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule notifications for 10 and 30 minutes before the timeSelected
|
||||
await _scheduleNotificationForTimeVIP(
|
||||
timeSelected.subtract(const Duration(minutes: 10)), // 10 minutes before
|
||||
title,
|
||||
message,
|
||||
details,
|
||||
1, // Unique ID for 10-minute before notification
|
||||
);
|
||||
|
||||
await _scheduleNotificationForTimeVIP(
|
||||
timeSelected.subtract(const Duration(minutes: 30)), // 30 minutes before
|
||||
title,
|
||||
message,
|
||||
details,
|
||||
2, // Unique ID for 30-minute before notification
|
||||
);
|
||||
|
||||
Log.print('Notifications scheduled successfully for the time selected');
|
||||
}
|
||||
|
||||
Future<void> _scheduleNotificationForTimeVIP(
|
||||
DateTime scheduledDate,
|
||||
String title,
|
||||
String message,
|
||||
NotificationDetails details,
|
||||
int notificationId,
|
||||
) async {
|
||||
// Initialize and set Cairo timezone
|
||||
tz.initializeTimeZones();
|
||||
var cairoLocation = tz.getLocation('Africa/Cairo');
|
||||
|
||||
final now = tz.TZDateTime.now(cairoLocation);
|
||||
|
||||
// Convert to Cairo time
|
||||
tz.TZDateTime scheduledTZDateTime =
|
||||
tz.TZDateTime.from(scheduledDate, cairoLocation);
|
||||
|
||||
// Check if 10 minutes before the scheduled time is in the past
|
||||
if (scheduledTZDateTime
|
||||
.subtract(const Duration(minutes: 10))
|
||||
.isBefore(now)) {
|
||||
// If the 10 minutes before the scheduled time is in the past, don't schedule
|
||||
Log.print(
|
||||
'Scheduled time minus 10 minutes is in the past. Skipping notification.');
|
||||
return; // Skip this notification
|
||||
}
|
||||
|
||||
Log.print('Current time (Cairo): $now');
|
||||
Log.print('Scheduling notification for: $scheduledTZDateTime');
|
||||
|
||||
await _flutterLocalNotificationsPlugin.zonedSchedule(
|
||||
id: notificationId, // Unique ID for each notification
|
||||
title: title,
|
||||
body: message,
|
||||
scheduledDate: scheduledTZDateTime,
|
||||
notificationDetails: details,
|
||||
androidScheduleMode: AndroidScheduleMode.exact,
|
||||
// uiLocalNotificationDateInterpretation:
|
||||
// UILocalNotificationDateInterpretation.absoluteTime,
|
||||
matchDateTimeComponents:
|
||||
null, // Don't repeat automatically; we handle manually
|
||||
);
|
||||
|
||||
Log.print('Notification scheduled successfully for: $scheduledTZDateTime');
|
||||
}
|
||||
|
||||
Future<void> _scheduleNotificationForTime(
|
||||
int dayOffset,
|
||||
int hour,
|
||||
int minute,
|
||||
String title,
|
||||
String message,
|
||||
NotificationDetails details,
|
||||
int notificationId,
|
||||
) async {
|
||||
// Initialize and set Cairo timezone
|
||||
tz.initializeTimeZones();
|
||||
var cairoLocation = tz.getLocation('Africa/Cairo');
|
||||
|
||||
final now = tz.TZDateTime.now(cairoLocation);
|
||||
tz.TZDateTime scheduledDate = tz.TZDateTime(
|
||||
cairoLocation,
|
||||
now.year,
|
||||
now.month,
|
||||
now.day + dayOffset, // Add offset to schedule for the next days
|
||||
hour,
|
||||
minute,
|
||||
);
|
||||
|
||||
// If the scheduled time is in the past, move it to the next day
|
||||
if (scheduledDate.isBefore(now)) {
|
||||
scheduledDate = scheduledDate.add(const Duration(days: 1));
|
||||
}
|
||||
|
||||
Log.print('Current time (Cairo): $now');
|
||||
Log.print('Scheduling notification for: $scheduledDate');
|
||||
|
||||
await _flutterLocalNotificationsPlugin.zonedSchedule(
|
||||
id: notificationId, // Unique ID for each notification
|
||||
title: title,
|
||||
body: message,
|
||||
scheduledDate: scheduledDate,
|
||||
notificationDetails: details,
|
||||
androidScheduleMode: AndroidScheduleMode.exact,
|
||||
// uiLocalNotificationDateInterpretation:
|
||||
// UILocalNotificationDateInterpretation.absoluteTime,
|
||||
matchDateTimeComponents:
|
||||
null, // Don't repeat automatically; we handle 7 days manually
|
||||
);
|
||||
|
||||
Log.print('Notification scheduled successfully for: $scheduledDate');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user