This is a follow up commit to `768ab3e9e4f55b872253d55c53983c19ab4c3d8b` in issue #1531 Testing ------- **Device:** iPhone 14 Pro simulator **iOS:** 17.0 **Damus:** This commit **Steps:** 1. Remove all relays. 2. Add the Damus relay. 3. Add `wss://relay.snort.social/` relay **(with trailing slash)**. Shows up on the relay list. (PASS) 4. Add `wss://relay.snort.social/v1` and `wss://relay.snort.social/v2` to the list. Both show up as separate relays (PASS) 4. Watch logs and wait for the relay list event to be sent out 5. Restart Damus (to help ensure the repro is stable) 6. Try removing the Snort relay by swiping. Relay is removed successfully (PASS) 7. Try removing the "v1" relay by clicking on "Disconnect relay" in the detail page. "v1" relay (and NOT "v2") is removed (PASS) 8. Try adding `nos.lol` from the recommended list. Added successfully. (PASS) 9. Remove `nos.lol` with a long press. (PASS) Changelog-Fixed: Fix issue where relays with trailing slashes cannot be removed (#1531) Closes: https://github.com/damus-io/damus/issues/1531 Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Signed-off-by: William Casarin <jb55@jb55.com>
83 lines
2.3 KiB
Swift
83 lines
2.3 KiB
Swift
//
|
||
// RelayURL.swift
|
||
// damus
|
||
//
|
||
// Created by Daniel D’Aquino on 2023-09-29.
|
||
//
|
||
|
||
import Foundation
|
||
|
||
public struct RelayURL: Hashable, Equatable, Codable, CodingKeyRepresentable {
|
||
private(set) var url: URL
|
||
|
||
var id: String {
|
||
return url.absoluteString
|
||
}
|
||
|
||
init?(_ str: String) {
|
||
guard let last = str.last else { return nil }
|
||
|
||
guard let url = URL(string: str) else {
|
||
return nil
|
||
}
|
||
|
||
guard let scheme = url.scheme else {
|
||
return nil
|
||
}
|
||
|
||
guard scheme == "ws" || scheme == "wss" else {
|
||
return nil
|
||
}
|
||
|
||
self.url = url
|
||
}
|
||
|
||
// MARK: - Codable
|
||
public init(from decoder: Decoder) throws {
|
||
let container = try decoder.singleValueContainer()
|
||
let urlString = try container.decode(String.self)
|
||
guard let instance = RelayURL(urlString) else {
|
||
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid URL string.")
|
||
}
|
||
self = instance
|
||
}
|
||
|
||
public func encode(to encoder: Encoder) throws {
|
||
var container = encoder.singleValueContainer()
|
||
try container.encode(url.absoluteString)
|
||
}
|
||
|
||
// MARK: - CodingKeyRepresentable
|
||
// CodingKeyRepresentable conformance is necessary to ensure that
|
||
// a dictionary with type "[RelayURL: T] where T: Codable" can be encoded into a keyed container
|
||
// e.g. `{<URL>: <VALUE>, <URL>: <VALUE>}` instead of `[<URL>, <VALUE>, <URL>, <VALUE>]`, which is Swift's default for non-string-keyed dictionaries
|
||
|
||
public var codingKey: CodingKey {
|
||
return StringKey(stringValue: self.url.absoluteString)
|
||
}
|
||
|
||
public init?<T>(codingKey: T) where T : CodingKey {
|
||
self.init(codingKey.stringValue)
|
||
}
|
||
|
||
// MARK: - Equatable
|
||
public static func == (lhs: RelayURL, rhs: RelayURL) -> Bool {
|
||
return lhs.url == rhs.url
|
||
}
|
||
|
||
// MARK: - Hashable
|
||
public func hash(into hasher: inout Hasher) {
|
||
hasher.combine(self.url)
|
||
}
|
||
|
||
}
|
||
|
||
private struct StringKey: CodingKey {
|
||
var stringValue: String
|
||
init(stringValue: String) {
|
||
self.stringValue = stringValue
|
||
}
|
||
var intValue: Int? { return nil }
|
||
init?(intValue: Int) { return nil }
|
||
}
|