Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion damus/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ class NostrNetworkManager {
var connectedRelays: [RelayPool.Relay] {
self.pool.relays
}

@MainActor
var signal: SignalModel {
self.pool.signal
}
Comment on lines +250 to +253

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add docstring to document the property's purpose.

This new computed property lacks documentation. According to coding guidelines, all added or modified code must have docstring coverage.

📝 Proposed docstring
+    /// The current network signal model reflecting relay connectivity status.
+    ///
+    /// This property forwards the relay pool's signal state, providing access
+    /// to the number of connected relays (`signal`) and total relays (`max_signal`)
+    /// for UI components and observers.
     `@MainActor`
     var signal: SignalModel {

As per coding guidelines: "Ensure docstring coverage for any code added or modified"

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@MainActor
var signal: SignalModel {
self.pool.signal
}
/// The current network signal model reflecting relay connectivity status.
///
/// This property forwards the relay pool's signal state, providing access
/// to the number of connected relays (`signal`) and total relays (`max_signal`)
/// for UI components and observers.
`@MainActor`
var signal: SignalModel {
self.pool.signal
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@damus/Core/Networking/NostrNetworkManager/NostrNetworkManager.swift` around
lines 250 - 253, Add a concise docstring above the `@MainActor` computed property
`signal` describing its purpose (exposing the network pool's signaling model),
its thread-affinity (main actor), and what it returns (the `SignalModel` from
`pool.signal`), e.g. one or two sentences that mention it forwards to
`pool.signal` for consumers to observe network signals; place the docstring
immediately above `var signal` in `NostrNetworkManager`.


@MainActor
var ourRelayDescriptors: [RelayPool.RelayDescriptor] {
Expand Down
16 changes: 16 additions & 0 deletions damus/Core/Nostr/RelayPool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
}
Comment on lines +129 to +139

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add docstring to document the method's purpose and behavior.

This new method lacks documentation. According to coding guidelines, all added or modified code must have docstring coverage.

📝 Proposed docstring
+    /// Updates the signal model with current relay connection statistics.
+    ///
+    /// Computes the number of connected relays and total relay count, then updates
+    /// `signal.signal` and `signal.max_signal` if the values have changed.
+    /// This reduces unnecessary publishes to observers when values are unchanged.
     `@MainActor`
     func update_signal() {

As per coding guidelines: "Ensure docstring coverage for any code added or modified"

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@damus/Core/Nostr/RelayPool.swift` around lines 129 - 139, The method
update_signal lacks a docstring; add a concise Swift doc comment above the
`@MainActor` func update_signal() that explains its purpose (synchronizes the
signal fields with current connection state), describes behavior (reads
num_connected and relays.count, updates signal.signal and signal.max_signal only
when values change), mentions thread context (`@MainActor`) and any side effects
on signal, and notes expected invariants (e.g., non-negative counts); reference
the symbols update_signal, signal, num_connected, and relays in the comment for
clarity.


func remove_handler(sub_id: String) {
self.handlers = handlers.filter {
if $0.sub_id != sub_id {
Expand Down Expand Up @@ -183,6 +196,7 @@ class RelayPool {

i += 1
}
update_signal()
}

/// Acquires a lease on ephemeral relays to prevent them from being cleaned up
Expand Down Expand 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.
Expand Down Expand Up @@ -761,6 +776,7 @@ class RelayPool {
run_queue(relay_id)
await self.resubscribeAll(relayId: relay_id)
}
await update_signal()
}

// Handle auth
Expand Down
63 changes: 54 additions & 9 deletions damus/Features/Relays/Views/SignalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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..<Self.num_bars, id: \.self) { i in
RoundedRectangle(cornerRadius: 1)
.fill(i < active_bars ? active_color : inactive_color)
.frame(width: Self.bar_width, height: Self.bar_heights[i])
}
}
}
.frame(width:50,height:30)
.disabled(signal.signal == signal.max_signal)
.frame(width: 30, height: 30)
.accessibilityLabel(Text("\(signal.signal)/\(signal.max_signal) relays connected"))
}
}

}
}

struct SignalView_Previews: PreviewProvider {
static var previews: some View {
SignalView(state: test_damus_state, signal: SignalModel(signal: 5, max_signal: 10))
HStack(spacing: 20) {
SignalView(state: test_damus_state, signal: SignalModel(signal: 0, max_signal: 10))
SignalView(state: test_damus_state, signal: SignalModel(signal: 3, max_signal: 10))
SignalView(state: test_damus_state, signal: SignalModel(signal: 5, max_signal: 10))
SignalView(state: test_damus_state, signal: SignalModel(signal: 10, max_signal: 10))
}
}
}
14 changes: 0 additions & 14 deletions damus/Features/Timeline/Models/HomeModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ class HomeModel: ContactsDelegate, ObservableObject {

@Published var loading: Bool = true

var signal = SignalModel()

var notifications = NotificationsModel()
var notification_status = NotificationStatusModel()
var events: EventHolder = EventHolder()
Expand Down Expand Up @@ -1037,18 +1035,6 @@ class HomeModel: ContactsDelegate, ObservableObject {
}


func update_signal_from_pool(signal: SignalModel, pool: RelayPool) async {
let relayCount = await pool.relays.count
if signal.max_signal != relayCount {
signal.max_signal = relayCount
}

let numberOfConnectedRelays = await pool.num_connected
if signal.signal != numberOfConnectedRelays {
signal.signal = numberOfConnectedRelays
}
}

@MainActor
func add_contact_if_friend(contacts: Contacts, ev: NostrEvent) {
if !contacts.is_friend(ev.pubkey) {
Expand Down
2 changes: 1 addition & 1 deletion damus/Features/Timeline/Views/PostingTimelineView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ struct PostingTimelineView: View {
Spacer()

HStack(alignment: .center) {
SignalView(state: damus_state, signal: home.signal)
SignalView(state: damus_state, signal: damus_state.nostrNetwork.signal)
if damus_state.settings.enable_favourites_feature {
Image(systemName: "square.stack")
.foregroundColor(DamusColors.purple)
Expand Down
4 changes: 2 additions & 2 deletions nostrdb/Test/NdbTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ final class NdbTests: XCTestCase {
}

for elem in tag {
print("tag[\(tags)][\(elem.index)]")
//print("tag[\(tags)][\(elem.index)]")
total_count_iter += 1
}

Expand Down Expand Up @@ -294,7 +294,7 @@ final class NdbTests: XCTestCase {

for tag in note.tags {
for elem in tag {
print("iter_elem \(elem.string())")
//print("iter_elem \(elem.string())")
for c in elem {
if char_count == 0 {
let ac = AsciiCharacter(c)
Expand Down
Loading