Update: 2026-07-30 02:27:45

This commit is contained in:
Hamza-Ayed
2026-07-30 02:27:45 +03:00
parent 5f62455113
commit ca4a7c2e70
56 changed files with 3391 additions and 709 deletions
@@ -10,11 +10,18 @@ import SwiftUI
import ActivityKit
// ─── 1. Data Model ───────────────────────────────────
//
// Must stay identical to InvoiceBatchAttributes in Runner/AppDelegate.swift and
// to the "content-state" keys the server sends in
// NotificationService::dispatchLiveActivityUpdate(). A mismatch makes iOS drop
// the update silently.
struct InvoiceBatchAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
var current: Int
var total: Int
var isDone: Bool
var failed: Int = 0
var statusText: String = ""
}
var companyName: String
}
@@ -27,7 +34,31 @@ struct MusadaqLiveActivityBundle: WidgetBundle {
}
}
// ─── 3. Widget ───────────────────────────────────────
// ─── 3. Copy helpers ─────────────────────────────────
// A partially failed batch must not read as a clean success.
private func headline(for state: InvoiceBatchAttributes.ContentState) -> String {
if state.isDone {
if state.failed > 0 && state.current > 0 { return "⚠️ اكتمل مع أخطاء" }
if state.failed > 0 { return "❌ فشلت المعالجة" }
return "✅ تم بنجاح"
}
return state.statusText.isEmpty
? "مُصادَق — جارٍ الرفع..."
: "مُصادَق — \(state.statusText)"
}
private func subtitle(for state: InvoiceBatchAttributes.ContentState, company: String) -> String {
var text = "\(state.current) / \(state.total) فاتورة"
if state.failed > 0 {
text += " — \(state.failed) فاشلة"
}
if !company.isEmpty {
text += " — \(company)"
}
return text
}
// ─── 4. Widget ───────────────────────────────────────
struct InvoiceBatchLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: InvoiceBatchAttributes.self) { context in
@@ -40,15 +71,17 @@ struct InvoiceBatchLiveActivity: Widget {
.foregroundColor(Color(red: 0.831, green: 0.659, blue: 0.263)) // #D4A843
.font(.title2)
VStack(alignment: .leading, spacing: 4) {
Text(context.state.isDone ? "✅ تم الرفع بنجاح" : "مُصادَق — جارٍ الرفع...")
Text(headline(for: context.state))
.font(.caption.bold())
.foregroundColor(.white)
ProgressView(
value: Double(context.state.current),
total: Double(context.state.total)
value: Double(context.state.current + context.state.failed),
total: Double(max(context.state.total, 1))
)
.tint(Color(red: 0.831, green: 0.659, blue: 0.263))
Text("\(context.state.current) / \(context.state.total) فاتورة — \(context.attributes.companyName)")
.tint(context.state.failed > 0
? Color(red: 0.96, green: 0.62, blue: 0.04)
: Color(red: 0.831, green: 0.659, blue: 0.263))
Text(subtitle(for: context.state, company: context.attributes.companyName))
.font(.caption2)
.foregroundColor(.gray)
}
@@ -66,8 +99,10 @@ struct InvoiceBatchLiveActivity: Widget {
.font(.caption.bold()).foregroundColor(.white)
}
DynamicIslandExpandedRegion(.bottom) {
ProgressView(value: Double(context.state.current),
total: Double(context.state.total))
// Divide-by-zero guard: total arrives as 0 if a push lands
// before the first image is registered.
ProgressView(value: Double(context.state.current + context.state.failed),
total: Double(max(context.state.total, 1)))
.tint(Color(red: 0.831, green: 0.659, blue: 0.263))
}
} compactLeading: {