diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 4649c043..6ce97e9e 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -252,7 +252,7 @@ struct ContentView: View { ToolbarItem(placement: .navigationBarTrailing) { HStack(alignment: .center) { - SignalView(state: damus_state!, signal: home.signal) + SignalView(state: damus_state!, signal: damus_state!.nostrNetwork.signal) // maybe expand this to other timelines in the future if selected_timeline == .search { diff --git a/damus/Core/Networking/NostrNetworkManager/NostrNetworkManager.swift b/damus/Core/Networking/NostrNetworkManager/NostrNetworkManager.swift index 57bf423c..fc29fb08 100644 --- a/damus/Core/Networking/NostrNetworkManager/NostrNetworkManager.swift +++ b/damus/Core/Networking/NostrNetworkManager/NostrNetworkManager.swift @@ -246,6 +246,11 @@ class NostrNetworkManager { var connectedRelays: [RelayPool.Relay] { self.pool.relays } + + @MainActor + var signal: SignalModel { + self.pool.signal + } @MainActor var ourRelayDescriptors: [RelayPool.RelayDescriptor] { diff --git a/damus/Core/Nostr/RelayPool.swift b/damus/Core/Nostr/RelayPool.swift index 6922e695..c82ce883 100644 --- a/damus/Core/Nostr/RelayPool.swift +++ b/damus/Core/Nostr/RelayPool.swift @@ -51,6 +51,7 @@ class RelayPool { var message_received_function: (((String, RelayDescriptor)) -> Void)? var message_sent_function: (((String, Relay)) -> Void)? var delegate: Delegate? + @MainActor private(set) var signal: SignalModel = SignalModel() /// Tracks active leases on ephemeral relays to prevent premature cleanup. @@ -125,6 +126,18 @@ class RelayPool { return relays.reduce(0) { n, r in n + (r.connection.isConnected ? 1 : 0) } } + @MainActor + func update_signal() { + let connected = num_connected + let total = relays.count + if signal.signal != connected { + signal.signal = connected + } + if signal.max_signal != total { + signal.max_signal = total + } + } + func remove_handler(sub_id: String) { self.handlers = handlers.filter { if $0.sub_id != sub_id { @@ -183,6 +196,7 @@ class RelayPool { i += 1 } + update_signal() } /// Acquires a lease on ephemeral relays to prevent them from being cleaned up @@ -269,6 +283,7 @@ class RelayPool { @MainActor private func appendRelayToList(relay: Relay) { self.relays.append(relay) + update_signal() } /// Ensures the given relay URLs are connected, adding them as ephemeral relays if not already in the pool. @@ -761,6 +776,7 @@ class RelayPool { run_queue(relay_id) await self.resubscribeAll(relayId: relay_id) } + await update_signal() } // Handle auth diff --git a/damus/Features/Relays/Views/SignalView.swift b/damus/Features/Relays/Views/SignalView.swift index 1cea7c46..4dff7c04 100644 --- a/damus/Features/Relays/Views/SignalView.swift +++ b/damus/Features/Relays/Views/SignalView.swift @@ -10,25 +10,70 @@ import SwiftUI struct SignalView: View { let state: DamusState @ObservedObject var signal: SignalModel - + + static let num_bars = 4 + static let bar_heights: [CGFloat] = [4, 7, 10, 13] + static let bar_width: CGFloat = 3 + static let bar_spacing: CGFloat = 2 + + var ratio: Double { + guard signal.max_signal > 0 else { return 0 } + return Double(signal.signal) / Double(signal.max_signal) + } + + var active_bars: Int { + if signal.signal == 0 { return 0 } + return max(1, min(Self.num_bars, Int(ceil(ratio * Double(Self.num_bars))))) + } + + var active_color: Color { + if ratio < 0.5 { + let t = ratio * 2.0 + return Color( + red: 1.0, + green: 0.4 + 0.4 * t, + blue: 0.4 + ) + } else { + let t = (ratio - 0.5) * 2.0 + return Color( + red: 1.0 - 0.6 * t, + green: 0.8, + blue: 0.4 + ) + } + } + + var inactive_color: Color { + Color.gray.opacity(0.3) + } + var body: some View { Group { - if signal.signal != signal.max_signal { + if signal.max_signal > 0 && signal.signal < signal.max_signal { NavigationLink(value: Route.RelayConfig) { - Text("\(signal.signal)/\(signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.") - .font(.callout) - .foregroundColor(.gray) + HStack(alignment: .bottom, spacing: Self.bar_spacing) { + ForEach(0..