Files
tripz-llc/apps/rider/ios/RideWidget/RideWidgetLiveActivity.swift
T
Hamza-AyedandClaude Opus 5 4d8414c96b 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>
2026-07-27 05:14:13 +03:00

201 lines
8.4 KiB
Swift

import ActivityKit
import WidgetKit
import SwiftUI
// 1️⃣ Attributes (كما هو مع Plugin: ContentState فارغ والقراءة من AppGroup)
struct LiveActivitiesAppAttributes: ActivityAttributes, Identifiable {
public typealias LiveDeliveryData = ContentState
public struct ContentState: Codable, Hashable { }
var id = UUID()
}
// 2️⃣ Prefix helper
extension LiveActivitiesAppAttributes {
func prefixedKey(_ key: String) -> String {
return "\(id)_\(key)"
}
}
// 3️⃣ Shared App Group
let sharedDefault = UserDefaults(suiteName: "group.com.Siro.siro")!
@available(iOS 16.1, *)
struct RideWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: LiveActivitiesAppAttributes.self) { context in
// ===== Read from shared defaults =====
let status = sharedDefault.string(forKey: context.attributes.prefixedKey("status")) ?? "waiting"
let driverName = sharedDefault.string(forKey: context.attributes.prefixedKey("driverName")) ?? "السائق"
let carDetails = sharedDefault.string(forKey: context.attributes.prefixedKey("carDetails")) ?? ""
let etaText = sharedDefault.string(forKey: context.attributes.prefixedKey("etaText")) ?? "--"
let progressRaw = sharedDefault.double(forKey: context.attributes.prefixedKey("progress"))
// Clamp progress (0..1)
let progress = min(max(progressRaw, 0.0), 1.0)
// ===== Production Lock Screen UI (White background) =====
ZStack {
RoundedRectangle(cornerRadius: 18)
.fill(Color.white)
VStack(alignment: .leading, spacing: 12) {
// Header
HStack(alignment: .center, spacing: 10) {
// App icon badge (clean)
ZStack {
RoundedRectangle(cornerRadius: 10)
.fill(Color.black.opacity(0.04))
.frame(width: 38, height: 38)
Image("SiroIcon")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 26, height: 26)
}
VStack(alignment: .leading, spacing: 3) {
Text(status == "waiting" ? "السائق في الطريق إليك" : "الرحلة جارية")
.font(.headline)
.foregroundColor(.black)
Text("\(driverName) • \(carDetails)")
.font(.subheadline)
.foregroundColor(.gray)
.lineLimit(1)
}
Spacer()
Text(etaText)
.font(.title2)
.bold()
.foregroundColor(.green)
.minimumScaleFactor(0.7)
}
// Progress bar with big car
GeometryReader { geometry in
let barHeight: CGFloat = 10
let carSize: CGFloat = 26
let usableWidth = max(0, geometry.size.width - carSize)
let x = min(max(0, usableWidth * CGFloat(progress)), usableWidth)
ZStack(alignment: .leading) {
RoundedRectangle(cornerRadius: 6)
.fill(Color.black.opacity(0.08))
.frame(height: barHeight)
RoundedRectangle(cornerRadius: 6)
.fill(Color.green)
.frame(width: max(0, geometry.size.width * CGFloat(progress)),
height: barHeight)
// Car marker (bigger + slightly above the bar)
Image(systemName: "car.side.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: carSize, height: carSize)
.foregroundColor(.black)
.background(
Circle()
.fill(Color.white)
.frame(width: carSize + 8, height: carSize + 8)
.shadow(color: Color.black.opacity(0.12), radius: 4, x: 0, y: 2)
)
.offset(x: x, y: -10)
}
}
.frame(height: 34)
// Footer micro status (optional but production-ish)
HStack(spacing: 6) {
Circle()
.fill(status == "waiting" ? Color.orange : Color.green)
.frame(width: 8, height: 8)
Text(status == "waiting" ? "بانتظار وصول السائق" : "أنت الآن في الرحلة")
.font(.caption)
.foregroundColor(.gray)
Spacer()
}
}
.padding(.horizontal, 14)
.padding(.vertical, 14)
}
.padding(.horizontal, 8)
} dynamicIsland: { context in
// ===== Read again for dynamic island =====
let status = sharedDefault.string(forKey: context.attributes.prefixedKey("status")) ?? "waiting"
let driverName = sharedDefault.string(forKey: context.attributes.prefixedKey("driverName")) ?? "السائق"
let carDetails = sharedDefault.string(forKey: context.attributes.prefixedKey("carDetails")) ?? ""
let etaText = sharedDefault.string(forKey: context.attributes.prefixedKey("etaText")) ?? "--"
let progressRaw = sharedDefault.double(forKey: context.attributes.prefixedKey("progress"))
let progress = min(max(progressRaw, 0.0), 1.0)
return DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
HStack(spacing: 6) {
Image("SiroIcon")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.clipShape(RoundedRectangle(cornerRadius: 5))
Image(systemName: "car.side.fill")
.foregroundColor(.green)
}
}
DynamicIslandExpandedRegion(.trailing) {
VStack(alignment: .trailing, spacing: 2) {
Text(etaText)
.font(.headline)
.foregroundColor(.green)
Text(status == "waiting" ? "قادم إليك" : "جاري")
.font(.caption)
.foregroundColor(.gray)
}
}
DynamicIslandExpandedRegion(.center) {
VStack(spacing: 4) {
Text(status == "waiting" ? "السائق في الطريق" : "الرحلة جارية")
.font(.subheadline)
ProgressView(value: progress)
.progressViewStyle(.linear)
}
}
DynamicIslandExpandedRegion(.bottom) {
Text("\(driverName) • \(carDetails)")
.font(.caption)
.foregroundColor(.gray)
.lineLimit(1)
}
} compactLeading: {
Image("SiroIcon")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 18, height: 18)
.clipShape(RoundedRectangle(cornerRadius: 4))
} compactTrailing: {
Text(etaText)
.font(.caption2)
.minimumScaleFactor(0.7)
} minimal: {
Image(systemName: "car.side.fill")
.foregroundColor(.green)
}
}
}
}