From 0719e94fbcb49b6b313d41c32d6c3592675f8068 Mon Sep 17 00:00:00 2001 From: ericholguin Date: Thu, 7 Mar 2024 15:28:08 +0000 Subject: [PATCH] ux: Relay View Improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch removes the Recommended Relay View and the old representation of recommended relays. Adds a tab view style to the Relay Config View allowing the user to switch between their connected relays and recommended relays. They can add and remove from the recommended view as well. For users logged in with a pubkey the add button will no longer be displayed. Testing —— iPhone 15 Pro Max (17.0) Light Mode: https://v.nostr.build/QGMZ.mp4 iPhone SE (3rd generation) (16.4) Dark Mode: https://v.nostr.build/Wlw3.mp4 —— Changelog-Changed: Relay config view user interface Signed-off-by: ericholguin Link: 20240307152808.47929-1-ericholguin@apache.org Signed-off-by: William Casarin --- damus.xcodeproj/project.pbxproj | 4 - damus/Util/Router.swift | 2 +- damus/Views/Relays/RecommendedRelayView.swift | 131 ----------- damus/Views/Relays/RelayConfigView.swift | 220 ++++++++---------- damus/Views/Relays/RelayDetailView.swift | 171 +++++++------- damus/Views/Relays/RelayPicView.swift | 2 +- damus/Views/Relays/RelayStatusView.swift | 1 - damus/Views/Relays/RelayView.swift | 140 +++++++---- damus/Views/UserRelaysView.swift | 5 +- 9 files changed, 295 insertions(+), 381 deletions(-) delete mode 100644 damus/Views/Relays/RecommendedRelayView.swift 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.")) } }