aboutsummaryrefslogtreecommitdiff
path: root/Hotline/State/AppState.swift
blob: e268ba2c0f5f472361f5af6b5b20fee1253ea937 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import SwiftUI

extension EnvironmentValues {
  @Entry var appState: AppState = AppState.shared
}

@Observable
final class AppState {
  static let shared = AppState()

  private init() {

  }
  
  var bonjourState = BonjourState()

  var activeHotline: HotlineState? = nil
  var activeServerState: ServerState? = nil

  var cloudKitReady: Bool = false

  // MARK: - Transfers

  /// All active transfers across all servers
  /// Transfers persist even if you disconnect from the server
  var transfers: [TransferInfo] = []
  
  @ObservationIgnored private var transferClients: [UUID: HotlineTransferClient] = [:]

  /// Track download tasks by reference number for cancellation
  @ObservationIgnored private var transferTasks: [UUID: Task<Void, Never>] = [:]
  
  /// Add a transfer to the transfer list
  @MainActor
  func addTransfer(_ transfer: TransferInfo) {
    self.transfers.append(transfer)
  }

  /// Cancel a transfer by transfer ID
  @MainActor
  func cancelTransfer(id: UUID) {
    guard let transferIndex = self.transfers.firstIndex(where: { $0.id == id }) else {
      return
    }
    
    // Cancel the task if it exists
    if let task = self.transferTasks[id] {
      task.cancel()
      self.transferTasks.removeValue(forKey: id)
    }
    
    if let client = self.transferClients[id] {
      client.cancel()
      self.transferClients.removeValue(forKey: id)
    }

    // Remove from transfers list
    self.transfers.remove(at: transferIndex)
  }
  
  /// Cancel specified transfers
  @MainActor
  func cancelTransfers(ids: [UUID]) {
    for transferID in ids {
      self.cancelTransfer(id: transferID)
    }
  }

  /// Cancel all active transfers
  @MainActor
  func cancelAllTransfers() {
    for (_, task) in self.transferTasks {
      task.cancel()
    }
    self.transferTasks.removeAll()
    
    for (_, client) in self.transferClients {
      client.cancel()
    }
    self.transferClients.removeAll()

    // Clear transfers
    self.transfers.removeAll()
  }
  
  /// Remove all completed transfers
  @MainActor
  func sweepTransfers() {
    for t in self.transfers {
      if t.done {
        self.cancelTransfer(id: t.id)
      }
    }
  }

  /// Register a transfer task
  @MainActor
  func registerTransferTask(_ task: Task<Void, Never>, transferID: UUID) {
    self.transferTasks[transferID] = task
  }
  
  @MainActor
  func registerTransferTask(_ task: Task<Void, Never>, transferID: UUID, client: HotlineTransferClient) {
    self.transferTasks[transferID] = task
    self.transferClients[transferID] = client
  }
  
  /// Unregister a download task (called on completion/failure)
  @MainActor
  func unregisterTransferTask(for transferID: UUID) {
    self.transferTasks.removeValue(forKey: transferID)
    self.transferClients.removeValue(forKey: transferID)
  }
}