Files
damus/damus/Views/Relays/RelayDetailView.swift
Daniel D’Aquino e951370a76 Fix relay URL trailing slash issues
This commit tries to replace all usage of `String` to represent relay
URLs and use `RelayURL` which automatically converts strings to a
canonical relay URL format that is more reliable and avoids issues related to
trailing slashes.

Test 1: Main issue fix
-----------------------

PASS

Device: iPhone 15 Simulator
iOS: 17.4
Damus: This commit
Steps:
1. Delete all connected relays
2. Add `wss://relay.damus.io/` (with the trailing slash) to the relay list
3. Try to post. Post should succeed. PASS
4. Try removing this newly added relay. Relay should be removed successfully. PASS

Test 2: Persistent relay list after upgrade
--------------------------------------------

PASS

Device: iPhone 15 Simulator
iOS: 17.4
Damus: 1.8 (1) `247f313b` + This commit
Steps:
1. Downgrade to old version
2. Add some relays to the list, some without a trailing slash, some with
3. Upgrade to this commit
4. All relays added in step 2 should still be there, and ones with a trailing slash should have been corrected to remove the trailing slash

Test 3: Miscellaneous regression tests
--------------------------------------

Device: iPhone 15 Simulator
iOS: 17.4
Damus: This commit
Coverage:
1. Posting works
2. Search works
3. Relay connection status works
4. Adding relays work
5. Removing relays work
6. Adding relay with trailing slashes works (it fixes itself to remove the trailing slash)
7. Adding relays with different paths works (e.g. wss://yabu.me/v1 and wss://yabu.me/v2)
8. Adding duplicate relay (but with trailing slash) gets rejected as expected
9. Relay details page works. All items on that view loads correctly
10. Relay logs work
11. Getting follower counts and seeing follow lists on profiles still work
12. Relay list changes persist after app restart
13. Notifications view still work
14. Copying the user's pubkey and profile link works
15. Share note + copy link button still works
16. Connecting NWC wallet works
17. One-tap zaps work
18. Onboarding works
19. Unit tests all passing

Closes: https://github.com/damus-io/damus/issues/2072
Changelog-Fixed: Fix bug that would cause connection issues with relays defined with a trailing slash URL, and an inability to delete them.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2024-03-25 09:24:17 +01:00

206 lines
9.1 KiB
Swift

//
// RelayDetailView.swift
// damus
//
// Created by Joel Klabo on 2/1/23.
//
import SwiftUI
struct RelayDetailView: View {
let state: DamusState
let relay: RelayURL
let nip11: RelayMetadata?
@ObservedObject var log: RelayLog
@Environment(\.dismiss) var dismiss
init(state: DamusState, relay: RelayURL, 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 new_ev = remove_relay( ev: ev, current_relays: descriptors, keypair: keypair, relay: relay) else {
return
}
process_contact_event(state: state, ev: new_ev)
state.postbox.send(new_ev)
if let relay_metadata = make_relay_metadata(relays: state.pool.our_descriptors, keypair: keypair) {
state.postbox.send(relay_metadata)
}
dismiss()
}) {
Text("Disconnect From Relay", comment: "Button to disconnect from the relay.")
}
}
var body: some View {
NavigationView {
ZStack {
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 ev_after_add = add_relay(ev: ev_before_add, keypair: keypair, current_relays: state.pool.our_descriptors, relay: relay, info: .rw) else {
return
}
process_contact_event(state: state, ev: ev_after_add)
state.postbox.send(ev_after_add)
if let relay_metadata = make_relay_metadata(relays: state.pool.our_descriptors, keypair: keypair) {
state.postbox.send(relay_metadata)
}
dismiss()
}) {
Text("Connect To Relay", comment: "Button to connect to the relay.")
}
}
}
if let authentication_state: RelayAuthenticationState = relay_object?.authentication_state,
authentication_state != .none {
Section(NSLocalizedString("Authentication", comment: "Header label to display authentication details for a given relay.")) {
RelayAuthenticationDetail(state: authentication_state)
}
}
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.absoluteString)
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.absoluteString)
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: BackNav())
.ignoresSafeArea(.all)
}
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_object: Relay? {
state.pool.get_relay(relay)
}
private var relay_connection: RelayConnection? {
relay_object?.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: RelayURL("wss://relay.damus.io")!, nip11: metadata)
}
}