Summary ------- This fixes the issue at Github #1531 where relays with trailing slashes cannot be removed. The root cause (Identified by @fishcakeday) was that for a relay to be removed, a certain dictionary entry containing the relay url needed to be removed prior to sending the updated relay list. However those dictionary keys used `String` objects, which cannot tell that two URLs are the same with or without a trailing slash. To fix the issue, I have used a dictionary with the type `[RelayURL: RelayInfo]`, and made the necessary protocol conformance implementations for RelayURL. This way, URLs are handled with higher accuracy (e.g. Trailing slashes do not matter, URLs that resolve to the same location will match no matter what). This allows us to leverage the existing parsing and handling logic that comes with the `URL` type, instead of manually handling URL strings. Generally speaking it is preferrable to work with higher level `URL` or `RelayURL` objects than handle URLs via `String`. There is an opportunity to refactor more code, but I intentionally kept the changes to `RelayURL` limited to the functionality in this issue, because otherwise the changeset becomes very big and risky. Issue reproduction ------------------ **Device:** iPhone 14 Pro simulator **iOS:** 17.0 **Damus:** Local build from `476f52562` with the following local change: ``` diff Signed-off-by: William Casarin <jb55@jb55.com>
182 lines
7.3 KiB
Swift
182 lines
7.3 KiB
Swift
//
|
|
// RelayDetailView.swift
|
|
// damus
|
|
//
|
|
// Created by Joel Klabo on 2/1/23.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct RelayDetailView: View {
|
|
let state: DamusState
|
|
let relay: String
|
|
let nip11: RelayMetadata?
|
|
|
|
@ObservedObject var log: RelayLog
|
|
|
|
@Environment(\.dismiss) var dismiss
|
|
|
|
init(state: DamusState, relay: String, nip11: RelayMetadata?) {
|
|
self.state = state
|
|
self.relay = relay
|
|
self.nip11 = nip11
|
|
|
|
log = state.relay_model_cache.model(with_relay_id: relay)?.log ?? RelayLog()
|
|
}
|
|
|
|
func check_connection() -> Bool {
|
|
for relay in state.pool.relays {
|
|
if relay.id == self.relay {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func FieldText(_ str: String?) -> some View {
|
|
if let s = str {
|
|
return Text(verbatim: s)
|
|
} else {
|
|
return Text("No data available", comment: "Text indicating that there is no data available to show for specific metadata about a relay server.")
|
|
}
|
|
}
|
|
|
|
func RemoveRelayButton(_ keypair: FullKeypair) -> some View {
|
|
Button(action: {
|
|
guard let ev = state.contacts.event else {
|
|
return
|
|
}
|
|
|
|
let descriptors = state.pool.our_descriptors
|
|
guard let relay_url = RelayURL(relay),
|
|
let new_ev = remove_relay( ev: ev, current_relays: descriptors, keypair: keypair, relay: relay_url) else {
|
|
return
|
|
}
|
|
|
|
process_contact_event(state: state, ev: new_ev)
|
|
state.postbox.send(new_ev)
|
|
dismiss()
|
|
}) {
|
|
Text("Disconnect From Relay", comment: "Button to disconnect from the relay.")
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
Group {
|
|
Form {
|
|
if let keypair = state.keypair.to_full() {
|
|
if check_connection() {
|
|
RemoveRelayButton(keypair)
|
|
} else {
|
|
Button(action: {
|
|
guard let ev_before_add = state.contacts.event else {
|
|
return
|
|
}
|
|
guard let relay_url = RelayURL(relay),
|
|
let ev_after_add = add_relay(ev: ev_before_add, keypair: keypair, current_relays: state.pool.our_descriptors, relay: relay_url, info: .rw) else {
|
|
return
|
|
}
|
|
process_contact_event(state: state, ev: ev_after_add)
|
|
state.postbox.send(ev_after_add)
|
|
dismiss()
|
|
}) {
|
|
Text("Connect To Relay", comment: "Button to connect to the relay.")
|
|
}
|
|
}
|
|
}
|
|
|
|
if let pubkey = nip11?.pubkey {
|
|
Section(NSLocalizedString("Admin", comment: "Label to display relay contact user.")) {
|
|
UserViewRow(damus_state: state, pubkey: pubkey)
|
|
.onTapGesture {
|
|
state.nav.push(route: Route.ProfileByKey(pubkey: pubkey))
|
|
}
|
|
}
|
|
}
|
|
|
|
if let relay_connection {
|
|
Section(NSLocalizedString("Relay", comment: "Label to display relay address.")) {
|
|
HStack {
|
|
Text(relay)
|
|
Spacer()
|
|
RelayStatusView(connection: relay_connection)
|
|
}
|
|
}
|
|
}
|
|
|
|
if let nip11 {
|
|
if nip11.is_paid {
|
|
Section(content: {
|
|
RelayPaidDetail(payments_url: nip11.payments_url)
|
|
}, header: {
|
|
Text("Paid Relay", comment: "Section header that indicates the relay server requires payment.")
|
|
}, footer: {
|
|
Text("This is a paid relay, you must pay for notes to be accepted.", comment: "Footer description that explains that the relay server requires payment to post.")
|
|
})
|
|
}
|
|
|
|
Section(NSLocalizedString("Description", comment: "Label to display relay description.")) {
|
|
FieldText(nip11.description)
|
|
}
|
|
Section(NSLocalizedString("Contact", comment: "Label to display relay contact information.")) {
|
|
FieldText(nip11.contact)
|
|
}
|
|
Section(NSLocalizedString("Software", comment: "Label to display relay software.")) {
|
|
FieldText(nip11.software)
|
|
}
|
|
Section(NSLocalizedString("Version", comment: "Label to display relay software version.")) {
|
|
FieldText(nip11.version)
|
|
}
|
|
if let nips = nip11.supported_nips, nips.count > 0 {
|
|
Section(NSLocalizedString("Supported NIPs", comment: "Label to display relay's supported NIPs.")) {
|
|
Text(nipsList(nips: nips))
|
|
}
|
|
}
|
|
}
|
|
|
|
if state.settings.developer_mode {
|
|
Section(NSLocalizedString("Log", comment: "Label to display developer mode logs.")) {
|
|
Text(log.contents ?? NSLocalizedString("No logs to display", comment: "Label to indicate that there are no developer mode logs available to be displayed on the screen"))
|
|
.font(.system(size: 13))
|
|
.lineLimit(nil)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.onReceive(handle_notify(.switched_timeline)) { notif in
|
|
dismiss()
|
|
}
|
|
.navigationTitle(nip11?.name ?? relay)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
|
|
private func nipsList(nips: [Int]) -> AttributedString {
|
|
var attrString = AttributedString()
|
|
let lastNipIndex = nips.count - 1
|
|
for (index, nip) in nips.enumerated() {
|
|
if let link = NIPURLBuilder.url(forNIP: nip) {
|
|
let nipString = NIPURLBuilder.formatNipNumber(nip: nip)
|
|
var nipAttrString = AttributedString(stringLiteral: nipString)
|
|
nipAttrString.link = link
|
|
attrString = attrString + nipAttrString
|
|
if index < lastNipIndex {
|
|
attrString = attrString + AttributedString(stringLiteral: ", ")
|
|
}
|
|
}
|
|
}
|
|
return attrString
|
|
}
|
|
|
|
private var relay_connection: RelayConnection? {
|
|
state.pool.get_relay(relay)?.connection
|
|
}
|
|
}
|
|
|
|
struct RelayDetailView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
let metadata = RelayMetadata(name: "name", description: "desc", pubkey: test_pubkey, contact: "contact", supported_nips: [1,2,3], software: "software", version: "version", limitation: Limitations.empty, payments_url: "https://jb55.com", icon: "")
|
|
RelayDetailView(state: test_damus_state, relay: "relay", nip11: metadata)
|
|
}
|
|
}
|