Compare commits

..

No commits in common. "d5d1c35b55ed48edb6964f2fcb2b2b35b2e3ff2d" and "88a3a9d6e013d5dc4718a14ebc26b296b579b857" have entirely different histories.

7 changed files with 23 additions and 32 deletions

View file

@ -691,14 +691,14 @@ actor APIService {
func getAvatarUrl() async throws -> String? { func getAvatarUrl() async throws -> String? {
let json = try await getJSON("/auth/avatar.php") let json = try await getJSON("/auth/avatar.php")
if IS_DEV { print("[Avatar] Response: \(json)") } print("[Avatar] Response: \(json)")
guard ok(json) else { guard ok(json) else {
if IS_DEV { print("[Avatar] Response not OK") } print("[Avatar] Response not OK")
return nil return nil
} }
let data = json["DATA"] as? [String: Any] ?? json let data = json["DATA"] as? [String: Any] ?? json
if IS_DEV { print("[Avatar] Data: \(data)") } print("[Avatar] Data: \(data)")
// Try all possible key variations for avatar URL // Try all possible key variations for avatar URL
let keys = ["AVATAR_URL", "AVATARURL", "AvatarUrl", "avatarUrl", "avatar_url", let keys = ["AVATAR_URL", "AVATARURL", "AvatarUrl", "avatarUrl", "avatar_url",
@ -707,16 +707,16 @@ actor APIService {
for key in keys { for key in keys {
if let url = data[key] as? String, !url.isEmpty { if let url = data[key] as? String, !url.isEmpty {
let resolved = Self.resolvePhotoUrl(url) let resolved = Self.resolvePhotoUrl(url)
if IS_DEV { print("[Avatar] Found key '\(key)' with value: \(url) -> \(resolved)") } print("[Avatar] Found key '\(key)' with value: \(url) -> \(resolved)")
return resolved return resolved
} }
if let url = json[key] as? String, !url.isEmpty { if let url = json[key] as? String, !url.isEmpty {
let resolved = Self.resolvePhotoUrl(url) let resolved = Self.resolvePhotoUrl(url)
if IS_DEV { print("[Avatar] Found key '\(key)' in json with value: \(url) -> \(resolved)") } print("[Avatar] Found key '\(key)' in json with value: \(url) -> \(resolved)")
return resolved return resolved
} }
} }
if IS_DEV { print("[Avatar] No avatar URL found in response") } print("[Avatar] No avatar URL found in response")
return nil return nil
} }
@ -724,7 +724,7 @@ actor APIService {
func getUserAvatarUrl(userId: Int) async throws -> String? { func getUserAvatarUrl(userId: Int) async throws -> String? {
// Try the avatar endpoint with UserID // Try the avatar endpoint with UserID
let json = try await postJSON("/auth/avatar.php", payload: ["UserID": userId]) let json = try await postJSON("/auth/avatar.php", payload: ["UserID": userId])
if IS_DEV { print("[Avatar] getUserAvatarUrl(\(userId)) Response: \(json)") } print("[Avatar] getUserAvatarUrl(\(userId)) Response: \(json)")
let data = json["DATA"] as? [String: Any] ?? json let data = json["DATA"] as? [String: Any] ?? json
@ -733,15 +733,15 @@ actor APIService {
"UserPhotoUrl", "USERPHOTOURL", "userPhotoUrl"] "UserPhotoUrl", "USERPHOTOURL", "userPhotoUrl"]
for key in keys { for key in keys {
if let url = data[key] as? String, !url.isEmpty { if let url = data[key] as? String, !url.isEmpty {
if IS_DEV { print("[Avatar] Found avatar for userId \(userId): \(url)") } print("[Avatar] Found avatar for userId \(userId): \(url)")
return Self.resolvePhotoUrl(url) return Self.resolvePhotoUrl(url)
} }
if let url = json[key] as? String, !url.isEmpty { if let url = json[key] as? String, !url.isEmpty {
if IS_DEV { print("[Avatar] Found avatar for userId \(userId): \(url)") } print("[Avatar] Found avatar for userId \(userId): \(url)")
return Self.resolvePhotoUrl(url) return Self.resolvePhotoUrl(url)
} }
} }
if IS_DEV { print("[Avatar] No avatar found for userId \(userId), all keys: \(json.keys.sorted())") } print("[Avatar] No avatar found for userId \(userId), all keys: \(json.keys.sorted())")
return nil return nil
} }

View file

@ -89,7 +89,7 @@ final class BeaconScanner: NSObject, ObservableObject {
} }
} }
if IS_DEV { print("[BeaconScanner] Started scanning for \(BeaconShardPool.uuids.count) shard UUIDs, target ServicePointId: \(targetServicePointId)") } print("[BeaconScanner] Started scanning for \(BeaconShardPool.uuids.count) shard UUIDs, target ServicePointId: \(targetServicePointId)")
} }
func stopScanning() { func stopScanning() {
@ -137,12 +137,12 @@ final class BeaconScanner: NSObject, ObservableObject {
await MainActor.run { await MainActor.run {
self.resolvedBeacons[key] = servicePointId self.resolvedBeacons[key] = servicePointId
self.pendingResolutions.remove(key) self.pendingResolutions.remove(key)
if IS_DEV { print("[BeaconScanner] Resolved \(key) -> ServicePointId \(servicePointId)") } print("[BeaconScanner] Resolved \(key) -> ServicePointId \(servicePointId)")
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
self.pendingResolutions.remove(key) self.pendingResolutions.remove(key)
if IS_DEV { print("[BeaconScanner] Failed to resolve \(key): \(error)") } print("[BeaconScanner] Failed to resolve \(key): \(error)")
} }
} }
} }
@ -181,7 +181,7 @@ extension BeaconScanner: CLLocationManagerDelegate {
if rssiSamples.count >= minSamplesToConfirm { if rssiSamples.count >= minSamplesToConfirm {
let avg = Double(rssiSamples.reduce(0, +)) / Double(rssiSamples.count) let avg = Double(rssiSamples.reduce(0, +)) / Double(rssiSamples.count)
if IS_DEV { print("[BeaconScanner] Target beacon confirmed! Avg RSSI: \(avg)") } print("[BeaconScanner] Target beacon confirmed! Avg RSSI: \(avg)")
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
self?.onBeaconDetected(avg) self?.onBeaconDetected(avg)
} }
@ -210,6 +210,6 @@ extension BeaconScanner: CLLocationManagerDelegate {
} }
func locationManager(_ manager: CLLocationManager, didFailRangingFor constraint: CLBeaconIdentityConstraint, error: Error) { func locationManager(_ manager: CLLocationManager, didFailRangingFor constraint: CLBeaconIdentityConstraint, error: Error) {
if IS_DEV { print("[BeaconScanner] Ranging failed: \(error)") } print("[BeaconScanner] Ranging failed: \(error)")
} }
} }

View file

@ -128,7 +128,7 @@ struct AboutScreen: View {
aboutInfo = try await APIService.shared.getAboutInfo() aboutInfo = try await APIService.shared.getAboutInfo()
} catch { } catch {
// Use fallback on error // Use fallback on error
if IS_DEV { print("Failed to load about info: \(error)") } print("Failed to load about info: \(error)")
} }
isLoading = false isLoading = false
} }

View file

@ -13,7 +13,7 @@ struct BusinessSelectionScreen: View {
@State private var selectedBusiness: Employment? @State private var selectedBusiness: Employment?
@State private var debugText = "" @State private var debugText = ""
private let refreshTimer = Timer.publish(every: 5, on: .main, in: .common).autoconnect() private let refreshTimer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
var body: some View { var body: some View {
NavigationStack { NavigationStack {

View file

@ -20,7 +20,7 @@ struct MyTasksScreen: View {
FilterTab(value: "completed", label: "Done", icon: "checkmark.circle.fill"), FilterTab(value: "completed", label: "Done", icon: "checkmark.circle.fill"),
] ]
private let refreshTimer = Timer.publish(every: 5, on: .main, in: .common).autoconnect() private let refreshTimer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {

View file

@ -28,7 +28,6 @@ struct TaskDetailScreen: View {
@State private var showCancelOrderAlert = false @State private var showCancelOrderAlert = false
@State private var isCancelingOrder = false @State private var isCancelingOrder = false
@State private var taskAccepted = false // Track if task was just accepted @State private var taskAccepted = false // Track if task was just accepted
@State private var hasCompleted = false // Guard against double-completion
@State private var customerAvatarUrl: String? // Fetched separately if not in task details @State private var customerAvatarUrl: String? // Fetched separately if not in task details
// Rating dialog // Rating dialog
@ -101,7 +100,6 @@ struct TaskDetailScreen: View {
} else if result == "cancelled" || result == "error" { } else if result == "cancelled" || result == "error" {
autoCompleting = false autoCompleting = false
beaconDetected = false beaconDetected = false
hasCompleted = false
beaconScanner?.resetSamples() beaconScanner?.resetSamples()
beaconScanner?.startScanning() beaconScanner?.startScanning()
} }
@ -631,7 +629,6 @@ struct TaskDetailScreen: View {
} }
.buttonStyle(.borderedProminent) .buttonStyle(.borderedProminent)
.tint(Color(red: 0.13, green: 0.55, blue: 0.13)) .tint(Color(red: 0.13, green: 0.55, blue: 0.13))
.disabled(hasCompleted)
} else { } else {
Button { showCompleteAlert = true } label: { Button { showCompleteAlert = true } label: {
Label("Complete Task", systemImage: "checkmark.circle.fill") Label("Complete Task", systemImage: "checkmark.circle.fill")
@ -640,7 +637,6 @@ struct TaskDetailScreen: View {
} }
.buttonStyle(.borderedProminent) .buttonStyle(.borderedProminent)
.tint(.green) .tint(.green)
.disabled(hasCompleted)
} }
} }
} }
@ -710,12 +706,12 @@ struct TaskDetailScreen: View {
} }
} }
if IS_DEV { print("[Beacon] showCompleteButton=\(showCompleteButton), servicePointId=\(d.servicePointId)") } print("[Beacon] showCompleteButton=\(showCompleteButton), servicePointId=\(d.servicePointId)")
if showCompleteButton && d.servicePointId > 0 { if showCompleteButton && d.servicePointId > 0 {
if IS_DEV { print("[Beacon] Starting beacon scanning for ServicePointId: \(d.servicePointId)") } print("[Beacon] Starting beacon scanning for ServicePointId: \(d.servicePointId)")
startBeaconScanning(d.servicePointId) startBeaconScanning(d.servicePointId)
} else { } else {
if IS_DEV { print("[Beacon] NOT starting scan - showCompleteButton=\(showCompleteButton), servicePointId=\(d.servicePointId)") } print("[Beacon] NOT starting scan - showCompleteButton=\(showCompleteButton), servicePointId=\(d.servicePointId)")
} }
} catch { } catch {
self.error = error.localizedDescription self.error = error.localizedDescription
@ -727,10 +723,9 @@ struct TaskDetailScreen: View {
let scanner = BeaconScanner( let scanner = BeaconScanner(
targetServicePointId: servicePointId, targetServicePointId: servicePointId,
onBeaconDetected: { [self] _ in onBeaconDetected: { [self] _ in
if !beaconDetected && !autoCompleting && !hasCompleted { if !beaconDetected && !autoCompleting {
beaconDetected = true beaconDetected = true
autoCompleting = true autoCompleting = true
hasCompleted = true
beaconScanner?.stopScanning() beaconScanner?.stopScanning()
showAutoCompleteDialog = true showAutoCompleteDialog = true
} }
@ -765,8 +760,6 @@ struct TaskDetailScreen: View {
} }
private func completeTask() { private func completeTask() {
guard !hasCompleted else { return }
hasCompleted = true
Task { Task {
do { do {
try await APIService.shared.completeTask(taskId: task.taskId) try await APIService.shared.completeTask(taskId: task.taskId)
@ -775,11 +768,9 @@ struct TaskDetailScreen: View {
if case .ratingRequired = apiError { if case .ratingRequired = apiError {
showRatingDialog = true showRatingDialog = true
} else { } else {
hasCompleted = false
self.error = apiError.localizedDescription self.error = apiError.localizedDescription
} }
} catch { } catch {
hasCompleted = false
self.error = error.localizedDescription self.error = error.localizedDescription
} }
} }

View file

@ -11,7 +11,7 @@ struct TaskListScreen: View {
@State private var selectedTask: WorkTask? @State private var selectedTask: WorkTask?
@State private var showingMyTasks = false @State private var showingMyTasks = false
private let refreshTimer = Timer.publish(every: 5, on: .main, in: .common).autoconnect() private let refreshTimer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
var body: some View { var body: some View {
ZStack(alignment: .bottomTrailing) { ZStack(alignment: .bottomTrailing) {