commit c4dfae9ede2b6d2892d81ee7bc5f264b32f2de95
parent bfda0d1b74d7910a989afcb7faa7a56f2017bff9
Author: ericholguin <eric.holguinsanchez@gmail.com>
Date: Fri, 8 Sep 2023 21:15:03 -0600
relays: update relay view to use new design
Changelog-Changed: Updated relay view
Closes: https://github.com/damus-io/damus/pull/1543
Diffstat:
5 files changed, 274 insertions(+), 204 deletions(-)
diff --git a/damus/Views/AddRelayView.swift b/damus/Views/AddRelayView.swift
@@ -8,33 +8,148 @@
import SwiftUI
struct AddRelayView: View {
- @Binding var relay: String
+ let state: DamusState
+ @State var new_relay: String = ""
+ @State var relayAddErrorTitle: String? = nil
+ @State var relayAddErrorMessage: String? = nil
+
+ @Environment(\.dismiss) var dismiss
var body: some View {
- ZStack(alignment: .leading) {
- HStack{
- TextField(NSLocalizedString("wss://some.relay.com", comment: "Placeholder example for relay server address."), text: $relay)
- .padding(2)
- .padding(.leading, 25)
+ VStack {
+ Text("Add relay", comment: "Title text to indicate user to an add a relay.")
+ .font(.system(size: 20, weight: .bold))
+ .padding(.vertical)
+
+ Divider()
+ .padding(.bottom)
+
+ HStack {
+ Label("", image: "copy2")
+ .onTapGesture {
+ if let pastedrelay = UIPasteboard.general.string {
+ self.new_relay = pastedrelay
+ }
+ }
+ TextField(NSLocalizedString("wss://some.relay.com", comment: "Placeholder example for relay server address."), text: $new_relay)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
Label("", image: "close-circle")
.foregroundColor(.accentColor)
- .padding(.trailing, -25.0)
- .opacity((relay == "") ? 0.0 : 1.0)
+ .opacity((new_relay == "") ? 0.0 : 1.0)
.onTapGesture {
- self.relay = ""
+ self.new_relay = ""
+ }
+ }
+ .padding(10)
+ .background(.secondary.opacity(0.2))
+ .cornerRadius(10)
+
+ if let errorMessage = relayAddErrorMessage {
+ VStack(spacing: 0) {
+ HStack(alignment: .top) {
+ Text(relayAddErrorTitle ?? "Error")
+ .bold()
+ .foregroundColor(DamusColors.dangerSecondary)
+ .padding(.leading)
+ Spacer()
+ Button(action: {
+ relayAddErrorTitle = nil // Clear error title
+ relayAddErrorMessage = nil // Clear error message
+ self.new_relay = ""
+ }, label: {
+ Image("close")
+ .frame(width: 20, height: 20)
+ .foregroundColor(DamusColors.dangerSecondary)
+ })
+ .padding(.trailing)
}
+
+ Text(errorMessage)
+ .foregroundColor(DamusColors.dangerSecondary)
+ .padding(.top, 10)
+ }
+ .frame(minWidth: 300, maxWidth: .infinity, minHeight: 120, alignment: .center)
+ .background {
+ RoundedRectangle(cornerRadius: 12)
+ .fill(DamusColors.dangerBorder, strokeBorder: .gray.opacity(0.5), lineWidth: 1)
+ }
}
- Label("", image: "copy2")
- .padding(.leading, -10)
- .onTapGesture {
- if let pastedrelay = UIPasteboard.general.string {
- self.relay = pastedrelay
+ Button(action: {
+ if new_relay.starts(with: "wss://") == false && new_relay.starts(with: "ws://") == false {
+ new_relay = "wss://" + new_relay
+ }
+
+ if new_relay.hasSuffix("/") {
+ new_relay.removeLast();
+ }
+
+ guard let url = RelayURL(new_relay),
+ let ev = state.contacts.event,
+ let keypair = state.keypair.to_full() else {
+ return
+ }
+
+ let info = RelayInfo.rw
+ let descriptor = RelayDescriptor(url: url, info: info)
+
+ do {
+ try state.pool.add_relay(descriptor)
+ relayAddErrorTitle = nil // Clear error title
+ relayAddErrorMessage = nil // Clear error message
+ } catch RelayError.RelayAlreadyExists {
+ relayAddErrorTitle = NSLocalizedString("Duplicate relay", comment: "Title of the duplicate relay error message.")
+ relayAddErrorMessage = NSLocalizedString("The relay you are trying to add is already added.\nYou're all set!", comment: "An error message that appears when the user attempts to add a relay that has already been added.")
+ return
+ } catch {
+ return
+ }
+
+ state.pool.connect(to: [new_relay])
+
+ guard let new_ev = add_relay(ev: ev, keypair: keypair, current_relays: state.pool.our_descriptors, relay: new_relay, info: info) else {
+ return
+ }
+
+ process_contact_event(state: state, ev: ev)
+
+ state.pool.send(.event(new_ev))
+
+ new_relay = ""
+
+ UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
+
+ dismiss()
+ }) {
+ HStack {
+ Text(verbatim: "Add relay")
+ .bold()
}
+ .frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
}
+ .buttonStyle(GradientButtonStyle(padding: 10))
+ //.disabled(!new_relay.isValidURL) <--- TODO
+ .padding(.vertical)
+
+ Spacer()
+ }
+ .padding()
+ }
+}
+
+// TODO
+// This works sometimes, in certain cases where the relay is valid it won't allow the user to add it
+// Needs improvement
+extension String {
+ var isValidURL: Bool {
+ let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
+ if let match = detector.firstMatch(in: self, options: [], range: NSRange(location: 0, length: self.utf16.count)) {
+ // it is a link, if the match covers the whole string
+ return match.range.length == self.utf16.count
+ } else {
+ return false
}
}
}
@@ -43,6 +158,6 @@ struct AddRelayView_Previews: PreviewProvider {
@State static var relay: String = ""
static var previews: some View {
- AddRelayView(relay: $relay)
+ AddRelayView(state: test_damus_state())
}
}
diff --git a/damus/Views/Relays/RecommendedRelayView.swift b/damus/Views/Relays/RecommendedRelayView.swift
@@ -12,64 +12,50 @@ struct RecommendedRelayView: View {
let relay: String
let add_button: Bool
+ @ObservedObject private var model_cache: RelayModelCache
+
@Binding var showActionButtons: Bool
init(damus: DamusState, relay: String, add_button: Bool = true, showActionButtons: Binding<Bool>) {
self.damus = damus
self.relay = relay
self.add_button = add_button
+ self.model_cache = damus.relay_model_cache
self._showActionButtons = showActionButtons
}
- var body: some View {
- ZStack {
- HStack {
- if let keypair = damus.keypair.to_full() {
- if showActionButtons && add_button {
- AddButton(keypair: keypair, showText: false)
- }
- }
-
- RelayType(is_paid: damus.relay_model_cache.model(with_relay_id: relay)?.metadata.is_paid ?? false)
-
- Text(relay).layoutPriority(1)
-
- 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)
- .disabled(showActionButtons)
-
- Spacer()
-
- Image("info")
- .resizable()
- .frame(width: 20, height: 20)
- .foregroundColor(Color.accentColor)
- } else {
- Spacer()
-
- Image("question")
- .resizable()
- .frame(width: 20, height: 20)
- .foregroundColor(.gray)
- }
+ var recommended: [RelayDescriptor] {
+ let rs: [RelayDescriptor] = []
+ return BOOTSTRAP_RELAYS.reduce(into: rs) { xs, x in
+ if damus.pool.get_relay(x) == nil, let url = RelayURL(x) {
+ xs.append(RelayDescriptor(url: url, info: .rw))
}
}
- .swipeActions {
- if add_button {
- if let keypair = damus.keypair.to_full() {
- AddButton(keypair: keypair, showText: false)
- .tint(.accentColor)
+ }
+
+ var body: some View {
+ VStack {
+ let meta = model_cache.model(with_relay_id: relay)?.metadata
+
+ 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)
+ .disabled(showActionButtons)
+ }
+
+ HStack {
+ Text(meta?.name ?? relay.hostname ?? relay)
+ .lineLimit(1)
+ }
+ .contextMenu {
+ CopyAction(relay: relay)
}
- }
- .contextMenu {
- CopyAction(relay: relay)
if let keypair = damus.keypair.to_full() {
- AddButton(keypair: keypair, showText: true)
+ AddButton(keypair: keypair)
}
}
}
@@ -82,19 +68,14 @@ struct RecommendedRelayView: View {
}
}
- func AddButton(keypair: FullKeypair, showText: Bool) -> some View {
+ func AddButton(keypair: FullKeypair) -> some View {
Button(action: {
add_action(keypair: keypair)
}) {
- if showText {
- Text(NSLocalizedString("Connect", comment: "Button to connect to recommended relay server."))
- }
- Image("plus-circle")
- .resizable()
- .frame(width: 20, height: 20)
- .foregroundColor(.accentColor)
- .padding(.leading, 5)
+ Text(NSLocalizedString("Add", comment: "Button to add relay server to list."))
+ .padding(10)
}
+ .buttonStyle(NeutralButtonStyle())
}
func add_action(keypair: FullKeypair) {
diff --git a/damus/Views/Relays/RelayConfigView.swift b/damus/Views/Relays/RelayConfigView.swift
@@ -9,10 +9,9 @@ import SwiftUI
struct RelayConfigView: View {
let state: DamusState
- @State var new_relay: String = ""
@State var relays: [RelayDescriptor]
@State private var showActionButtons = false
- @State var relayAddErrorMessage: String? = nil
+ @State var show_add_relay: Bool = false
@Environment(\.dismiss) var dismiss
@@ -41,118 +40,69 @@ struct RelayConfigView: View {
}
var MainContent: some View {
- Form {
- Section {
- AddRelayView(relay: $new_relay)
- } header: {
- HStack {
- Text(NSLocalizedString("Connect To Relay", comment: "Label for section for adding a relay server."))
- .font(.system(size: 18, weight: .heavy))
- .padding(.bottom, 5)
- }
- } footer: {
+ VStack {
+ Divider()
+
+ if recommended.count > 0 {
VStack {
- HStack {
- Spacer()
- if !new_relay.isEmpty {
- Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted relay.")) {
- new_relay = ""
- UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
- }
- .font(.system(size: 14, weight: .bold))
- .frame(width: 80, height: 30)
- .foregroundColor(.white)
- .background(LINEAR_GRADIENT)
- .clipShape(Capsule())
- .padding(EdgeInsets(top: 15, leading: 0, bottom: 0, trailing: 0))
-
- Button(NSLocalizedString("Add", comment: "Button to confirm adding user inputted relay.")) {
-
- if new_relay.starts(with: "wss://") == false && new_relay.starts(with: "ws://") == false {
- new_relay = "wss://" + new_relay
- }
-
- if new_relay.hasSuffix("/") {
- new_relay.removeLast();
- }
-
- guard let url = RelayURL(new_relay),
- let ev = state.contacts.event,
- let keypair = state.keypair.to_full() else {
- return
- }
-
- let info = RelayInfo.rw
- let descriptor = RelayDescriptor(url: url, info: info)
-
- do {
- try state.pool.add_relay(descriptor)
- relayAddErrorMessage = nil // Clear error message
- } catch RelayError.RelayAlreadyExists {
- relayAddErrorMessage = NSLocalizedString("This relay is already in your list", comment: "An error message that appears when the user attempts to add a relay that has already been added.")
- return
- } catch {
- return
- }
-
- state.pool.connect(to: [new_relay])
-
- guard let new_ev = add_relay(ev: ev, keypair: keypair, current_relays: state.pool.our_descriptors, relay: new_relay, info: info) else {
- return
- }
-
- process_contact_event(state: state, ev: ev)
-
- state.pool.send(.event(new_ev))
-
- new_relay = ""
-
- UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
- }
- .font(.system(size: 14, weight: .bold))
- .frame(width: 80, height: 30)
- .foregroundColor(.white)
- .background(LINEAR_GRADIENT)
- .clipShape(Capsule())
- .padding(EdgeInsets(top: 15, leading: 0, bottom: 0, trailing: 0))
+ Text("Recommended relays")
+ .foregroundStyle(DamusLightGradient.gradient)
+ .padding(10)
+ .background {
+ RoundedRectangle(cornerRadius: 15)
+ .stroke(DamusLightGradient.gradient)
}
- }
- if let errorMessage = relayAddErrorMessage {
- HStack {
- Spacer()
- Text(errorMessage)
- .foregroundColor(Color.red)
+ .padding(.vertical)
+
+ HStack(spacing: 20) {
+ ForEach(recommended, id: \.url) { r in
+ RecommendedRelayView(damus: state, relay: r.url.id, showActionButtons: $showActionButtons)
}
}
+ .padding()
}
- }
-
- Section {
- List(Array(relays), id: \.url) { relay in
- RelayView(state: state, relay: relay.url.id, showActionButtons: $showActionButtons)
- }
- } header: {
- HStack {
- Text(NSLocalizedString("Connected Relays", comment: "Section title for relay servers that are connected."))
- .font(.system(size: 18, weight: .heavy))
- .padding(.bottom, 5)
+ .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)
}
- if recommended.count > 0 {
- Section {
- List(recommended, id: \.url) { r in
- RecommendedRelayView(damus: state, relay: r.url.id, showActionButtons: $showActionButtons)
+ HStack {
+ Text(NSLocalizedString("My Relays", comment: "Section title for relay servers that the user is connected to."))
+ .font(.system(size: 32, weight: .bold))
+
+ Spacer()
+
+ Button(action: {
+ show_add_relay.toggle()
+ }) {
+ HStack {
+ Text(verbatim: "Add relay")
+ .padding(10)
}
- } header: {
- Text(NSLocalizedString("Recommended Relays", comment: "Section title for recommend relay servers that could be added as part of configuration"))
- .font(.system(size: 18, weight: .heavy))
- .padding(.bottom, 5)
}
+ .buttonStyle(NeutralButtonStyle())
}
+ .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(.large)
+ .navigationBarTitleDisplayMode(.inline)
+ .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)
+ }
+ }
.toolbar {
if state.keypair.privkey != nil {
if showActionButtons {
diff --git a/damus/Views/Relays/RelayStatusView.swift b/damus/Views/Relays/RelayStatusView.swift
@@ -13,21 +13,51 @@ struct RelayStatusView: View {
var body: some View {
Group {
if connection.isConnecting {
- ProgressView()
+ Text("Connecting")
+ .font(.caption)
+ .frame(height: 20)
+ .padding(.horizontal, 10)
+ .foregroundColor(DamusColors.warning)
+ .background(DamusColors.warningQuaternary)
+ .cornerRadius(20)
+ .overlay(
+ RoundedRectangle(cornerRadius: 20)
+ .stroke(DamusColors.warningBorder, lineWidth: 1)
+ )
+ } else if connection.isConnected {
+ Text("Online")
+ .font(.caption)
+ .frame(height: 20)
+ .padding(.horizontal, 10)
+ .foregroundColor(DamusColors.success)
+ .background(DamusColors.successQuaternary)
+ .cornerRadius(20)
+ .overlay(
+ RoundedRectangle(cornerRadius: 20)
+ .stroke(DamusColors.successBorder, lineWidth: 1)
+ )
} else {
- Image(connection.isConnected ? "globe" : "warning.fill")
- .resizable()
- .foregroundColor(connection.isConnected ? .green : .red)
+ Text("Error")
+ .font(.caption)
+ .frame(height: 20)
+ .padding(.horizontal, 10)
+ .foregroundColor(DamusColors.danger)
+ .background(DamusColors.dangerQuaternary)
+ .border(DamusColors.dangerBorder)
+ .cornerRadius(20)
+ .overlay(
+ RoundedRectangle(cornerRadius: 20)
+ .stroke(DamusColors.dangerBorder, lineWidth: 1)
+ )
}
}
- .frame(width: 20, height: 20)
- .padding(.trailing, 5)
+ .padding(.trailing, 20)
}
}
struct RelayStatusView_Previews: PreviewProvider {
static var previews: some View {
- let connection = test_damus_state().pool.get_relay("relay")!.connection
+ let connection = test_damus_state().pool.get_relay("wss://relay.damus.io")!.connection
RelayStatusView(connection: connection)
}
}
diff --git a/damus/Views/Relays/RelayView.swift b/damus/Views/Relays/RelayView.swift
@@ -28,41 +28,35 @@ struct RelayView: View {
if showActionButtons {
RemoveButton(privkey: privkey, showText: false)
}
- else if let relay_connection {
- RelayStatusView(connection: relay_connection)
- }
}
-
- RelayType(is_paid: state.relay_model_cache.model(with_relay_id: relay)?.metadata.is_paid ?? false)
-
- if let meta = model_cache.model(with_relay_id: relay)?.metadata {
- Text(relay)
- .background(
- NavigationLink(value: Route.RelayDetail(relay: relay, metadata: meta), label: {
- EmptyView()
- }).opacity(0.0).disabled(showActionButtons)
- )
-
- Spacer()
- Image("info")
- .resizable()
- .frame(width: 20, height: 20)
- .foregroundColor(Color.accentColor)
- } else {
+ let meta = model_cache.model(with_relay_id: relay)?.metadata
+
+ RelayPicView(relay: relay, icon: meta?.icon, size: 55, highlight: .none, disable_animation: false)
+
+ VStack(alignment: .leading) {
+ HStack {
+ Text(meta?.name ?? relay)
+ .font(.headline)
+ .padding(.bottom, 2)
+ RelayType(is_paid: state.relay_model_cache.model(with_relay_id: relay)?.metadata.is_paid ?? false)
+ }
Text(relay)
+ .font(.subheadline)
+ .foregroundColor(.gray)
+ }
+
+ Spacer()
+
+ if let relay_connection {
+ RelayStatusView(connection: relay_connection)
.background(
- NavigationLink(value: Route.RelayDetail(relay: relay, metadata: nil), label: {
+ NavigationLink(value: Route.RelayDetail(relay: relay, metadata: meta), label: {
EmptyView()
- }).opacity(0.0).disabled(showActionButtons)
+ })
+ .buttonStyle(.plain)
+ .disabled(showActionButtons)
)
-
- Spacer()
-
- Image("question")
- .resizable()
- .frame(width: 20, height: 20)
- .foregroundColor(.gray)
}
}
}