diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 45f4bb4a..230e76b7 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -288,7 +288,6 @@ 4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */; }; 4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; }; 4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; }; - 4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; }; 4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */; }; 4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838529656C8B00DC99E7 /* NIP05.swift */; }; 4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88388296AF99A00DC99E7 /* EventDetailBar.swift */; }; @@ -1205,7 +1204,6 @@ 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConfigView.swift; sourceTree = ""; }; 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = ""; }; 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = ""; }; - 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedRelayView.swift; sourceTree = ""; }; 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRelaysView.swift; sourceTree = ""; }; 4CB8838529656C8B00DC99E7 /* NIP05.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP05.swift; sourceTree = ""; }; 4CB88388296AF99A00DC99E7 /* EventDetailBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailBar.swift; sourceTree = ""; }; @@ -2294,7 +2292,6 @@ isa = PBXGroup; children = ( 4CE879532996BA0000F758CC /* Detail */, - 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */, 4C06670028FC7C5900038D2A /* RelayView.swift */, 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */, F7908E91298B0F0700AB113A /* RelayDetailView.swift */, @@ -3417,7 +3414,6 @@ 7527271E2A93FF0100214108 /* Block.swift in Sources */, 4C54AA0729A540BA003E4487 /* NotificationsModel.swift in Sources */, 4C12536C2A76D4B00004F4B8 /* RepostedNotify.swift in Sources */, - 4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */, 4CE4F0F229D4FCFA005914DB /* DebouncedOnChange.swift in Sources */, 4C32B9592A9AD44700DC3548 /* Table.swift in Sources */, 4C5D5C9D2A6B2CB40024563C /* AsciiCharacter.swift in Sources */, diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift index 6ac44882..90f0b622 100644 --- a/damus/Util/Router.swift +++ b/damus/Util/Router.swift @@ -53,7 +53,7 @@ enum Route: Hashable { case .Followers(let followers): FollowersView(damus_state: damusState, followers: followers) case .Relay(let relay, let showActionButtons): - RelayView(state: damusState, relay: relay, showActionButtons: showActionButtons) + RelayView(state: damusState, relay: relay, showActionButtons: showActionButtons, recommended: false) case .RelayDetail(let relay, let metadata): RelayDetailView(state: damusState, relay: relay, nip11: metadata) case .Following(let following): diff --git a/damus/Views/Relays/RecommendedRelayView.swift b/damus/Views/Relays/RecommendedRelayView.swift deleted file mode 100644 index 8ad0af14..00000000 --- a/damus/Views/Relays/RecommendedRelayView.swift +++ /dev/null @@ -1,131 +0,0 @@ -// -// RecommendedRelayView.swift -// damus -// -// Created by William Casarin on 2022-12-29. -// - -import SwiftUI - -struct RecommendedRelayView: View { - let damus: DamusState - let relay: String - let add_button: Bool - let user_recommended: Bool - - @ObservedObject private var model_cache: RelayModelCache - - init(damus: DamusState, relay: String, add_button: Bool = true, user_recommended: Bool = false) { - self.damus = damus - self.relay = relay - self.add_button = add_button - self.user_recommended = user_recommended - self.model_cache = damus.relay_model_cache - } - - var body: some View { - let meta = model_cache.model(with_relay_id: relay)?.metadata - - if user_recommended { - HStack { - RelayPicView(relay: relay, icon: meta?.icon, size: 50, highlight: .none, disable_animation: false) - .padding(.horizontal, 5) - - VStack(alignment: .leading) { - HStack { - Text(meta?.name ?? relay) - .font(.headline) - .padding(.bottom, 2) - - RelayType(is_paid: damus.relay_model_cache.model(with_relay_id: relay)?.metadata.is_paid ?? false) - } - - Text(relay) - .font(.subheadline) - .foregroundColor(.gray) - } - - Spacer() - - if let keypair = damus.keypair.to_full() { - VStack(alignment: .center) { - if damus.pool.get_relay(relay) == nil { - AddButton(keypair: keypair) - } else { - Image(systemName: "checkmark.circle") - .resizable() - .frame(width: 30, height: 30) - .foregroundColor(DamusColors.success) - .padding(.trailing, 10) - } - } - .padding(.horizontal, 5) - } - } - } else { - VStack { - RelayPicView(relay: relay, icon: meta?.icon, size: 70, highlight: .none, disable_animation: false) - if let meta = damus.relay_model_cache.model(with_relay_id: relay)?.metadata { - NavigationLink(value: Route.RelayDetail(relay: relay, metadata: meta)){ - EmptyView() - } - .opacity(0.0) - } - - HStack { - Text(meta?.name ?? relay) - .lineLimit(1) - .frame(maxWidth: 150) - .padding(.vertical, 5) - } - .contextMenu { - CopyAction(relay: relay) - } - - if let keypair = damus.keypair.to_full() { - AddButton(keypair: keypair) - } - } - } - } - - func CopyAction(relay: String) -> some View { - Button { - UIPasteboard.general.setValue(relay, forPasteboardType: "public.plain-text") - } label: { - Label(NSLocalizedString("Copy", comment: "Button to copy a relay server address."), image: "copy") - } - } - - func AddButton(keypair: FullKeypair) -> some View { - Button(action: { - add_action(keypair: keypair) - }) { - Text(NSLocalizedString("Add", comment: "Button to add relay server to list.")) - .padding(10) - } - .buttonStyle(NeutralButtonStyle()) - } - - func add_action(keypair: FullKeypair) { - guard let ev_before_add = damus.contacts.event else { - return - } - guard let relay_url = RelayURL(relay), - let ev_after_add = add_relay(ev: ev_before_add, keypair: keypair, current_relays: damus.pool.our_descriptors, relay: relay_url, info: .rw) else { - return - } - process_contact_event(state: damus, ev: ev_after_add) - damus.postbox.send(ev_after_add) - - if let relay_metadata = make_relay_metadata(relays: damus.pool.our_descriptors, keypair: keypair) { - damus.postbox.send(relay_metadata) - } - } -} - -struct RecommendedRelayView_Previews: PreviewProvider { - static var previews: some View { - RecommendedRelayView(damus: test_damus_state, relay: "wss://relay.damus.io", user_recommended: true) - } -} diff --git a/damus/Views/Relays/RelayConfigView.swift b/damus/Views/Relays/RelayConfigView.swift index 5ddb63fc..16bb49ab 100644 --- a/damus/Views/Relays/RelayConfigView.swift +++ b/damus/Views/Relays/RelayConfigView.swift @@ -7,156 +7,83 @@ import SwiftUI +enum RelayTab: Int, CaseIterable{ + case myRelays = 0 + case recommended + + var title: String{ + switch self { + case .myRelays: + return "My relays" + case .recommended: + return "Recommended" + } + } +} + struct RelayConfigView: View { let state: DamusState @State var relays: [RelayDescriptor] @State private var showActionButtons = false @State var show_add_relay: Bool = false - @SceneStorage("RelayConfigView.show_recommended") var show_recommended : Bool = true + @State var selectedTab = 0 @Environment(\.dismiss) var dismiss init(state: DamusState) { self.state = state _relays = State(initialValue: state.pool.our_descriptors) + UITabBar.appearance().isHidden = true } var recommended: [RelayDescriptor] { let rs: [RelayDescriptor] = [] let recommended_relay_addresses = get_default_bootstrap_relays() return recommended_relay_addresses.reduce(into: rs) { xs, x in - if state.pool.get_relay(x) == nil, let url = RelayURL(x) { + if let url = RelayURL(x) { xs.append(RelayDescriptor(url: url, info: .rw)) } } } var body: some View { - MainContent - .onReceive(handle_notify(.relays_changed)) { _ in - self.relays = state.pool.our_descriptors - } - .onReceive(handle_notify(.switched_timeline)) { _ in - dismiss() - } - } - - var MainContent: some View { - VStack { - Divider() - - if showActionButtons && !show_recommended { - VStack { - Button(action: { - withAnimation(.easeOut(duration: 0.2)) { - show_recommended.toggle() - } - }) { - Text("Show recommended relays", comment: "Button to show recommended relays.") - .foregroundStyle(DamusLightGradient.gradient) - .padding(10) - .background { - RoundedRectangle(cornerRadius: 15) - .stroke(DamusLightGradient.gradient) - } - } - .padding(.top, 10) - } - } - - if recommended.count > 0 && show_recommended { - VStack { - HStack(alignment: .top) { - Spacer() - Button(action: { - withAnimation(.easeOut(duration: 0.2)) { - show_recommended.toggle() - } - }) { - Image(systemName: "xmark.circle") - .font(.system(size: 18)) - .foregroundStyle(DamusLightGradient.gradient) - } - .padding([.top, .trailing], 8) - } - - Text("Recommended relays", comment: "Title for view of recommended relays.") - .foregroundStyle(DamusLightGradient.gradient) - .padding(10) - .background { - RoundedRectangle(cornerRadius: 15) - .stroke(DamusLightGradient.gradient) - } - - ScrollView(.horizontal) { - HStack(spacing: 20) { - ForEach(recommended, id: \.url) { r in - RecommendedRelayView(damus: state, relay: r.url.id) - } - } - .padding(.horizontal, 30) - .padding(.vertical, 5) - } - .scrollIndicators(.hidden) - .mask( - HStack(spacing: 0) { - LinearGradient(gradient: Gradient(colors: [Color.clear, Color.white]), startPoint: .leading, endPoint: .trailing) - .frame(width: 30) - - Rectangle() - .fill(Color.white) - .frame(maxWidth: .infinity) - - LinearGradient(gradient: Gradient(colors: [Color.white, Color.clear]), startPoint: .leading, endPoint: .trailing) - .frame(width: 30) - } - ) - .padding() - } - .frame(minWidth: 250, maxWidth: .infinity, minHeight: 250, alignment: .center) - .background { - RoundedRectangle(cornerRadius: 12) - .fill(DamusLightGradient.gradient.opacity(0.15), strokeBorder: DamusLightGradient.gradient, lineWidth: 1) - } - .padding(.horizontal) - } - - HStack { - Text(NSLocalizedString("My Relays", comment: "Section title for relay servers that the user is connected to.")) - .font(.system(size: 32, weight: .bold)) + NavigationView { + ZStack(alignment: .bottom){ + TabView(selection: $selectedTab) { + RelayList(title: "My Relays", relayList: relays, recommended: false) + .tag(0) - Spacer() - - Button(action: { - show_add_relay.toggle() - }) { - HStack { - Text(verbatim: "Add relay") - .padding(10) + RelayList(title: "Recommended", relayList: recommended, recommended: true) + .tag(1) + } + ZStack{ + HStack{ + ForEach((RelayTab.allCases), id: \.self){ item in + Button{ + selectedTab = item.rawValue + } label: { + CustomTabItem(title: item.title, isActive: (selectedTab == item.rawValue)) + } + } } } - .buttonStyle(NeutralButtonStyle()) + .frame(width: 235, height: 35) + .background(.damusNeutral3) + .cornerRadius(30) + .padding(.horizontal, 26) } - .padding(25) - - List(Array(relays), id: \.url) { relay in - RelayView(state: state, relay: relay.url.id, showActionButtons: $showActionButtons) - } - .listStyle(PlainListStyle()) } .navigationTitle(NSLocalizedString("Relays", comment: "Title of relays view")) .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .navigationBarItems(leading: BackNav()) .sheet(isPresented: $show_add_relay, onDismiss: { self.show_add_relay = false }) { - if #available(iOS 16.0, *) { - AddRelayView(state: state) - .presentationDetents([.height(300)]) - .presentationDragIndicator(.visible) - } else { - AddRelayView(state: state) - } + AddRelayView(state: state) + .presentationDetents([.height(300)]) + .presentationDragIndicator(.visible) } .toolbar { - if state.keypair.privkey != nil { + if state.keypair.privkey != nil && selectedTab == 0 { if showActionButtons { Button("Done") { withAnimation { @@ -172,6 +99,65 @@ struct RelayConfigView: View { } } } + .onReceive(handle_notify(.relays_changed)) { _ in + self.relays = state.pool.our_descriptors + } + .onAppear { + notify(.display_tabbar(false)) + } + .onDisappear { + notify(.display_tabbar(true)) + } + .ignoresSafeArea(.all) + } + + func RelayList(title: String, relayList: [RelayDescriptor], recommended: Bool) -> some View { + ScrollView(showsIndicators: false) { + HStack { + Text(NSLocalizedString(title, comment: "Section title for type of relay server list")) + .font(.system(size: 32, weight: .bold)) + + + Spacer() + + if state.keypair.privkey != nil { + Button(action: { + show_add_relay.toggle() + }) { + HStack { + Text(verbatim: "Add relay") + .padding(10) + } + } + .buttonStyle(NeutralButtonStyle()) + } + } + .padding(.top, 5) + + ForEach(relayList, id: \.url) { relay in + Group { + RelayView(state: state, relay: relay.url.id, showActionButtons: $showActionButtons, recommended: recommended) + Divider() + } + } + + Spacer() + .padding(25) + } + .padding(.horizontal) + } +} + +extension RelayConfigView{ + func CustomTabItem(title: String, isActive: Bool) -> some View { + HStack { + Text(title) + .font(.system(size: 12, weight: isActive ? .bold : .regular)) + .foregroundColor(isActive ? .damusAdaptableBlack : .damusAdaptableBlack.opacity(0.7)) + } + .frame(width: 110, height: 30) + .background(isActive ? .damusAdaptableWhite.opacity(0.9) : .clear) + .cornerRadius(30) } } diff --git a/damus/Views/Relays/RelayDetailView.swift b/damus/Views/Relays/RelayDetailView.swift index c88c68b4..08129cab 100644 --- a/damus/Views/Relays/RelayDetailView.swift +++ b/damus/Views/Relays/RelayDetailView.swift @@ -66,97 +66,101 @@ struct RelayDetailView: View { } 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 + 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 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) + + 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.") + } } - 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 + } + + 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) + 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.") + }) } - 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) + Section(NSLocalizedString("Description", comment: "Label to display relay description.")) { + FieldText(nip11.description) } - 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)) + 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 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) + } } } } - - 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 @@ -164,6 +168,9 @@ struct RelayDetailView: View { } .navigationTitle(nip11?.name ?? relay) .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .navigationBarItems(leading: BackNav()) + .ignoresSafeArea(.all) } private func nipsList(nips: [Int]) -> AttributedString { diff --git a/damus/Views/Relays/RelayPicView.swift b/damus/Views/Relays/RelayPicView.swift index a2875c46..c45c39d0 100644 --- a/damus/Views/Relays/RelayPicView.swift +++ b/damus/Views/Relays/RelayPicView.swift @@ -61,7 +61,7 @@ struct InnerRelayPicView: View { } .frame(width: size, height: size) .clipShape(RoundedRectangle(cornerRadius: 15)) - .overlay(RoundedRectangle(cornerRadius: 15).stroke(failedImage ? .gray : highlight_color(highlight), lineWidth: failedImage ? 1 : pfp_line_width(highlight))) + .overlay(RoundedRectangle(cornerRadius: 15).stroke(.gray.opacity(0.5), lineWidth: 0.5)) } } diff --git a/damus/Views/Relays/RelayStatusView.swift b/damus/Views/Relays/RelayStatusView.swift index 74a72f25..05151893 100644 --- a/damus/Views/Relays/RelayStatusView.swift +++ b/damus/Views/Relays/RelayStatusView.swift @@ -51,7 +51,6 @@ struct RelayStatusView: View { ) } } - .padding(.trailing, 20) } } diff --git a/damus/Views/Relays/RelayView.swift b/damus/Views/Relays/RelayView.swift index e6784adc..27c3b008 100644 --- a/damus/Views/Relays/RelayView.swift +++ b/damus/Views/Relays/RelayView.swift @@ -10,22 +10,31 @@ import SwiftUI struct RelayView: View { let state: DamusState let relay: String + let recommended: Bool @ObservedObject private var model_cache: RelayModelCache + @State var relay_state: Bool @Binding var showActionButtons: Bool - init(state: DamusState, relay: String, showActionButtons: Binding) { + init(state: DamusState, relay: String, showActionButtons: Binding, recommended: Bool) { self.state = state self.relay = relay + self.recommended = recommended self.model_cache = state.relay_model_cache _showActionButtons = showActionButtons + let relay_state = RelayView.get_relay_state(pool: state.pool, relay: relay) + self._relay_state = State(initialValue: relay_state) + } + + static func get_relay_state(pool: RelayPool, relay: String) -> Bool { + return pool.get_relay(relay) == nil } var body: some View { Group { HStack { if let privkey = state.keypair.privkey { - if showActionButtons { + if showActionButtons && !recommended { RemoveButton(privkey: privkey, showText: false) } } @@ -39,39 +48,60 @@ struct RelayView: View { Text(meta?.name ?? relay) .font(.headline) .padding(.bottom, 2) + .lineLimit(1) RelayType(is_paid: state.relay_model_cache.model(with_relay_id: relay)?.metadata.is_paid ?? false) } Text(relay) .font(.subheadline) .foregroundColor(.gray) + .lineLimit(1) + .contextMenu { + CopyAction(relay: relay) + + if let privkey = state.keypair.privkey { + RemoveButton(privkey: privkey, showText: true) + } + } } Spacer() + + if recommended { + if let keypair = state.keypair.to_full() { + VStack(alignment: .center) { + if relay_state { + AddButton(keypair: keypair) + } else { + Button(action: { + remove_action(privkey: keypair.privkey) + }) { + Text(NSLocalizedString("Added", comment: "Button to show relay server is already added to list.")) + .font(.caption) + } + .buttonStyle(NeutralButtonShape.capsule.style) + .opacity(0.5) + } + } + .padding(.horizontal, 5) + } + } else { + if let relay_connection { + RelayStatusView(connection: relay_connection) + } - if let relay_connection { - RelayStatusView(connection: relay_connection) - .background( - NavigationLink(value: Route.RelayDetail(relay: relay, metadata: meta), label: { - EmptyView() - }) - .buttonStyle(.plain) - .disabled(showActionButtons) - ) + Image("chevron-large-right") + .resizable() + .frame(width: 15, height: 15) + .foregroundColor(.gray) } } + .contentShape(Rectangle()) } - .swipeActions { - if let privkey = state.keypair.privkey { - RemoveButton(privkey: privkey, showText: false) - .tint(.red) - } + .onReceive(handle_notify(.relays_changed)) { _ in + self.relay_state = RelayView.get_relay_state(pool: state.pool, relay: self.relay) } - .contextMenu { - CopyAction(relay: relay) - - if let privkey = state.keypair.privkey { - RemoveButton(privkey: privkey, showText: true) - } + .onTapGesture { + state.nav.push(route: Route.RelayDetail(relay: relay, metadata: model_cache.model(with_relay_id: relay)?.metadata)) } } @@ -79,6 +109,52 @@ struct RelayView: View { state.pool.get_relay(relay)?.connection } + func add_action(keypair: FullKeypair) { + 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) + + if let relay_metadata = make_relay_metadata(relays: state.pool.our_descriptors, keypair: keypair) { + state.postbox.send(relay_metadata) + } + } + + func remove_action(privkey: Privkey) { + guard let ev = state.contacts.event else { + return + } + + let descriptors = state.pool.our_descriptors + guard let keypair = state.keypair.to_full(), + 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) + + if let relay_metadata = make_relay_metadata(relays: state.pool.our_descriptors, keypair: keypair) { + state.postbox.send(relay_metadata) + } + } + + func AddButton(keypair: FullKeypair) -> some View { + Button(action: { + add_action(keypair: keypair) + }) { + Text(NSLocalizedString("Add", comment: "Button to add relay server to list.")) + .font(.caption) + } + .buttonStyle(NeutralButtonShape.capsule.style) + } + func CopyAction(relay: String) -> some View { Button { UIPasteboard.general.setValue(relay, forPasteboardType: "public.plain-text") @@ -89,23 +165,7 @@ struct RelayView: View { func RemoveButton(privkey: Privkey, showText: Bool) -> some View { Button(action: { - guard let ev = state.contacts.event else { - return - } - - let descriptors = state.pool.our_descriptors - guard let keypair = state.keypair.to_full(), - 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) - - if let relay_metadata = make_relay_metadata(relays: state.pool.our_descriptors, keypair: keypair) { - state.postbox.send(relay_metadata) - } + remove_action(privkey: privkey) }) { if showText { Text(NSLocalizedString("Disconnect", comment: "Button to disconnect from a relay server.")) @@ -122,6 +182,6 @@ struct RelayView: View { struct RelayView_Previews: PreviewProvider { static var previews: some View { - RelayView(state: test_damus_state, relay: "wss://relay.damus.io", showActionButtons: .constant(false)) + RelayView(state: test_damus_state, relay: "wss://relay.damus.io", showActionButtons: .constant(false), recommended: false) } } diff --git a/damus/Views/UserRelaysView.swift b/damus/Views/UserRelaysView.swift index 336bb4d1..16099d67 100644 --- a/damus/Views/UserRelaysView.swift +++ b/damus/Views/UserRelaysView.swift @@ -28,12 +28,9 @@ struct UserRelaysView: View { var body: some View { List(relay_state, id: \.0) { (r, add) in - RecommendedRelayView(damus: state, relay: r, add_button: add, user_recommended: true) + RelayView(state: state, relay: r, showActionButtons: .constant(true), recommended: true) } .listStyle(PlainListStyle()) - .onReceive(handle_notify(.relays_changed)) { _ in - self.relay_state = UserRelaysView.make_relay_state(pool: state.pool, relays: self.relays) - } .navigationBarTitle(NSLocalizedString("Relays", comment: "Navigation bar title that shows the list of relays for a user.")) } }