Merge remote-tracking branch 'eric/relay-config-changes'

This commit is contained in:
William Casarin
2023-03-27 11:47:23 -04:00
10 changed files with 345 additions and 150 deletions

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "bitcoin-logo.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20px" height="20px" viewBox="0 0 20 20" version="1.1">
<g id="surface1">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(96.862745%,57.647059%,10.196078%);fill-opacity:1;" d="M 19.699219 12.417969 C 18.363281 17.777344 12.9375 21.035156 7.582031 19.699219 C 2.226562 18.363281 -1.035156 12.9375 0.300781 7.582031 C 1.636719 2.222656 7.0625 -1.035156 12.417969 0.300781 C 17.773438 1.632812 21.035156 7.0625 19.699219 12.417969 Z M 19.699219 12.417969 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 14.410156 8.574219 C 14.609375 7.246094 13.59375 6.53125 12.210938 6.050781 L 12.660156 4.25 L 11.5625 3.976562 L 11.125 5.730469 C 10.835938 5.660156 10.539062 5.589844 10.246094 5.523438 L 10.6875 3.757812 L 9.589844 3.484375 L 9.140625 5.285156 C 8.902344 5.230469 8.667969 5.179688 8.4375 5.121094 L 8.441406 5.117188 L 6.925781 4.738281 L 6.636719 5.910156 C 6.636719 5.910156 7.449219 6.097656 7.433594 6.109375 C 7.875 6.21875 7.957031 6.511719 7.941406 6.746094 L 7.429688 8.800781 C 7.460938 8.808594 7.5 8.820312 7.546875 8.835938 L 7.429688 8.808594 L 6.710938 11.683594 C 6.65625 11.820312 6.519531 12.023438 6.210938 11.945312 C 6.21875 11.960938 5.410156 11.746094 5.410156 11.746094 L 4.867188 13 L 6.296875 13.359375 C 6.5625 13.425781 6.820312 13.492188 7.078125 13.558594 L 6.621094 15.382812 L 7.71875 15.65625 L 8.167969 13.851562 C 8.46875 13.933594 8.757812 14.007812 9.042969 14.078125 L 8.59375 15.875 L 9.691406 16.148438 L 10.144531 14.328125 C 12.015625 14.683594 13.425781 14.539062 14.015625 12.847656 C 14.492188 11.484375 13.992188 10.699219 13.007812 10.1875 C 13.726562 10.019531 14.265625 9.550781 14.410156 8.574219 Z M 11.902344 12.089844 C 11.5625 13.453125 9.269531 12.71875 8.523438 12.53125 L 9.128906 10.117188 C 9.871094 10.300781 12.253906 10.667969 11.902344 12.089844 Z M 12.242188 8.554688 C 11.933594 9.796875 10.023438 9.164062 9.402344 9.011719 L 9.949219 6.820312 C 10.570312 6.976562 12.5625 7.261719 12.242188 8.554688 Z M 12.242188 8.554688 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -8,72 +8,41 @@
import SwiftUI
struct AddRelayView: View {
@Binding var show_add_relay: Bool
@Binding var relay: String
let action: (String?) -> Void
var body: some View {
VStack(alignment: .leading) {
Form {
Section(NSLocalizedString("Add Relay", comment: "Label for section for adding a relay server.")) {
ZStack(alignment: .leading) {
HStack{
TextField(NSLocalizedString("wss://some.relay.com", comment: "Placeholder example for relay server address."), text: $relay)
.padding(2)
.padding(.leading, 25)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
Label("", systemImage: "xmark.circle.fill")
.foregroundColor(.blue)
.padding(.trailing, -25.0)
.opacity((relay == "") ? 0.0 : 1.0)
.onTapGesture {
self.relay = ""
}
}
Label("", systemImage: "doc.on.clipboard")
.padding(.leading, -10)
.onTapGesture {
if let pastedrelay = UIPasteboard.general.string {
self.relay = pastedrelay
}
}
ZStack(alignment: .leading) {
HStack{
TextField(NSLocalizedString("wss://some.relay.com", comment: "Placeholder example for relay server address."), text: $relay)
.padding(2)
.padding(.leading, 25)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
Label("", systemImage: "xmark.circle.fill")
.foregroundColor(.accentColor)
.padding(.trailing, -25.0)
.opacity((relay == "") ? 0.0 : 1.0)
.onTapGesture {
self.relay = ""
}
}
}
VStack {
HStack {
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted relay.")) {
show_add_relay = false
action(nil)
}
.contentShape(Rectangle())
Spacer()
Button(NSLocalizedString("Add", comment: "Button to confirm adding user inputted relay.")) {
show_add_relay = false
action(relay)
relay = ""
}
.buttonStyle(.borderedProminent)
.contentShape(Rectangle())
Label("", systemImage: "doc.on.clipboard")
.padding(.leading, -10)
.onTapGesture {
if let pastedrelay = UIPasteboard.general.string {
self.relay = pastedrelay
}
.padding()
}
}
}
}
struct AddRelayView_Previews: PreviewProvider {
@State static var show: Bool = true
@State static var relay: String = ""
static var previews: some View {
AddRelayView(show_add_relay: $show, relay: $relay, action: {_ in })
AddRelayView(relay: $relay)
}
}

View File

@@ -12,22 +12,33 @@ struct RecommendedRelayView: View {
let relay: String
let add_button: Bool
init(damus: DamusState, relay: String) {
@Binding var showActionButtons: Bool
init(damus: DamusState, relay: String, showActionButtons: Binding<Bool>) {
self.damus = damus
self.relay = relay
self.add_button = true
self._showActionButtons = showActionButtons
}
init(damus: DamusState, relay: String, add_button: Bool) {
init(damus: DamusState, relay: String, add_button: Bool, showActionButtons: Binding<Bool>) {
self.damus = damus
self.relay = relay
self.add_button = add_button
self._showActionButtons = showActionButtons
}
var body: some View {
ZStack {
HStack {
if let privkey = damus.keypair.privkey {
if showActionButtons && add_button {
AddButton(privkey: privkey, showText: false)
}
}
RelayType(is_paid: damus.relay_metadata.lookup(relay_id: relay)?.is_paid ?? false)
Text(relay).layoutPriority(1)
if let meta = damus.relay_metadata.lookup(relay_id: relay) {
@@ -37,41 +48,75 @@ struct RecommendedRelayView: View {
EmptyView()
}
.opacity(0.0)
.disabled(showActionButtons)
Spacer()
Image(systemName: "info.circle")
.font(.system(size: 20, weight: .regular))
.foregroundColor(Color.accentColor)
} else {
Spacer()
Image(systemName: "questionmark.circle")
.font(.system(size: 20, weight: .regular))
.foregroundColor(.gray)
}
}
}
.swipeActions {
if add_button {
if let privkey = damus.keypair.privkey {
AddAction(privkey: privkey)
AddButton(privkey: privkey, showText: false)
.tint(.accentColor)
}
}
}
.contextMenu {
CopyAction(relay: relay)
if let privkey = damus.keypair.privkey {
AddButton(privkey: privkey, showText: true)
}
}
}
func AddAction(privkey: String) -> some View {
func CopyAction(relay: String) -> some View {
Button {
guard let ev_before_add = damus.contacts.event else {
return
}
guard let ev_after_add = add_relay(ev: ev_before_add, privkey: privkey, current_relays: damus.pool.descriptors, relay: relay, info: .rw) else {
return
}
process_contact_event(state: damus, ev: ev_after_add)
damus.pool.send(.event(ev_after_add))
UIPasteboard.general.setValue(relay, forPasteboardType: "public.plain-text")
} label: {
Label(NSLocalizedString("Add Relay", comment: "Button to add recommended relay server."), systemImage: "plus.circle")
Label(NSLocalizedString("Copy", comment: "Button to copy a relay server address."), systemImage: "doc.on.doc")
}
.tint(.accentColor)
}
func AddButton(privkey: String, showText: Bool) -> some View {
Button(action: {
add_action(privkey: privkey)
}) {
if showText {
Text(NSLocalizedString("Connect", comment: "Button to connect to recommended relay server."))
}
Image(systemName: "plus.circle.fill")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.accentColor)
.padding(.leading, 5)
}
}
func add_action(privkey: String) {
guard let ev_before_add = damus.contacts.event else {
return
}
guard let ev_after_add = add_relay(ev: ev_before_add, privkey: privkey, current_relays: damus.pool.descriptors, relay: relay, info: .rw) else {
return
}
process_contact_event(state: damus, ev: ev_after_add)
damus.pool.send(.event(ev_after_add))
}
}
struct RecommendedRelayView_Previews: PreviewProvider {
static var previews: some View {
RecommendedRelayView(damus: test_damus_state(), relay: "wss://relay.damus.io")
RecommendedRelayView(damus: test_damus_state(), relay: "wss://relay.damus.io", showActionButtons: .constant(false))
}
}

View File

@@ -10,8 +10,8 @@ import SwiftUI
struct RelayConfigView: View {
let state: DamusState
@State var new_relay: String = ""
@State var show_add_relay: Bool = false
@State var relays: [RelayDescriptor]
@State private var showActionButtons = false
@Environment(\.dismiss) var dismiss
@@ -37,72 +37,122 @@ struct RelayConfigView: View {
.onReceive(handle_notify(.switched_timeline)) { _ in
dismiss()
}
.sheet(isPresented: $show_add_relay) {
AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { m_relay in
guard var relay = m_relay else {
return
}
if relay.starts(with: "wss://") == false && relay.starts(with: "ws://") == false {
relay = "wss://" + relay
}
if relay.hasSuffix("/") {
relay.removeLast();
}
guard let url = URL(string: relay) else {
return
}
guard let ev = state.contacts.event else {
return
}
guard let privkey = state.keypair.privkey else {
return
}
let info = RelayInfo.rw
guard (try? state.pool.add_relay(url, info: info)) != nil else {
return
}
state.pool.connect(to: [relay])
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: relay, info: info) else {
return
}
process_contact_event(state: state, ev: ev)
state.pool.send(.event(new_ev))
}
}
}
var MainContent: some View {
Form {
Section {
List(Array(relays), id: \.url) { relay in
RelayView(state: state, relay: relay.url.absoluteString)
}
AddRelayView(relay: $new_relay)
} header: {
HStack {
Text("Relays", comment: "Header text for relay server list for configuration.")
Spacer()
Button(action: { show_add_relay = true }) {
Image(systemName: "plus")
.foregroundColor(.accentColor)
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 {
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 = URL(string: new_relay) else {
return
}
guard let ev = state.contacts.event else {
return
}
guard let privkey = state.keypair.privkey else {
return
}
let info = RelayInfo.rw
guard (try? state.pool.add_relay(url, info: info)) != nil else {
return
}
state.pool.connect(to: [new_relay])
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.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))
}
}
}
}
Section {
List(Array(relays), id: \.url) { relay in
RelayView(state: state, relay: relay.url.absoluteString, 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)
}
}
if recommended.count > 0 {
Section(NSLocalizedString("Recommended Relays", comment: "Section title for recommend relay servers that could be added as part of configuration")) {
Section {
List(recommended, id: \.url) { r in
RecommendedRelayView(damus: state, relay: r.url.absoluteString)
RecommendedRelayView(damus: state, relay: r.url.absoluteString, showActionButtons: $showActionButtons)
}
} 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)
}
}
}
.navigationTitle(NSLocalizedString("Relays", comment: "Title of relays view"))
.navigationBarTitleDisplayMode(.large)
.toolbar {
if state.keypair.privkey != nil {
if showActionButtons {
Button("Done") {
showActionButtons.toggle()
}
} else {
Button("Edit") {
showActionButtons.toggle()
}
}
}

View File

@@ -16,6 +16,15 @@ struct RelayDetailView: View {
@Environment(\.dismiss) var dismiss
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 {
Text(str ?? "No data available")
}
@@ -23,6 +32,42 @@ struct RelayDetailView: View {
var body: some View {
Group {
Form {
if let privkey = state.keypair.privkey {
if check_connection() {
Button(action: {
guard let ev = state.contacts.event else {
return
}
let descriptors = state.pool.descriptors
guard let new_ev = remove_relay( ev: ev, current_relays: descriptors, privkey: privkey, relay: relay) else {
return
}
process_contact_event(state: state, ev: new_ev)
state.pool.send(.event(new_ev))
dismiss()
}) {
Text("Disconnect From Relay", comment: "Button to disconnect from the relay.")
}
} else {
Button(action: {
guard let ev_before_add = state.contacts.event else {
return
}
guard let ev_after_add = add_relay(ev: ev_before_add, privkey: privkey, current_relays: state.pool.descriptors, relay: relay, info: .rw) else {
return
}
process_contact_event(state: state, ev: ev_after_add)
state.pool.send(.event(ev_after_add))
dismiss()
}) {
Text("Connect To Relay", comment: "Button to connect to the relay.")
}
}
}
if let pubkey = nip11.pubkey {
Section(NSLocalizedString("Admin", comment: "Label to display relay contact user.")) {
UserView(damus_state: state, pubkey: pubkey)
@@ -68,6 +113,7 @@ struct RelayDetailView: View {
dismiss()
}
.navigationTitle(nip11.name ?? "")
.navigationBarTitleDisplayMode(.inline)
}
private func nipsList(nips: [Int]) -> AttributedString {

View File

@@ -14,16 +14,20 @@ struct RelayStatus: View {
let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
@State var conn_color: Color = .gray
@State var conn_image: String = "network"
@State var connecting: Bool = false
func update_connection_color() {
func update_connection() {
for relay in pool.relays {
if relay.id == self.relay {
let c = relay.connection
if c.isConnected {
conn_image = "network"
conn_color = .green
} else if c.isConnecting || c.isReconnecting {
conn_color = .yellow
connecting = true
} else {
conn_image = "exclamationmark.circle.fill"
conn_color = .red
}
}
@@ -31,15 +35,25 @@ struct RelayStatus: View {
}
var body: some View {
Circle()
.frame(width: 8.0, height: 8.0)
.foregroundColor(conn_color)
.onReceive(timer) { _ in
update_connection_color()
}
.onAppear() {
update_connection_color()
HStack {
if connecting {
ProgressView()
.padding(.trailing, 4)
} else {
Image(systemName: conn_image)
.frame(width: 8.0, height: 8.0)
.foregroundColor(conn_color)
.padding(.leading, 5)
.padding(.trailing, 10)
}
}
.onReceive(timer) { _ in
update_connection()
}
.onAppear() {
update_connection()
}
}
}

View File

@@ -11,9 +11,12 @@ struct RelayType: View {
let is_paid: Bool
var body: some View {
Image(systemName: is_paid ? "dollarsign.circle.fill" : "globe.americas.fill")
.foregroundColor(is_paid ? Color("DamusGreen") : .gray)
if is_paid {
Image("bitcoin-logo")
} else {
Image(systemName: "globe.americas.fill")
.foregroundColor(.gray)
}
}
}

View File

@@ -11,32 +11,53 @@ struct RelayView: View {
let state: DamusState
let relay: String
@Binding var showActionButtons: Bool
var body: some View {
Group {
HStack {
RelayStatus(pool: state.pool, relay: relay)
RelayType(is_paid: state.relay_metadata.lookup(relay_id: relay)?.is_paid ?? false)
if let meta = state.relay_metadata.lookup(relay_id: relay) {
NavigationLink {
RelayDetailView(state: state, relay: relay, nip11: meta)
} label: {
Text(relay)
if let privkey = state.keypair.privkey {
if showActionButtons {
RemoveButton(privkey: privkey, showText: false)
}
else {
RelayStatus(pool: state.pool, relay: relay)
}
}
RelayType(is_paid: state.relay_metadata.lookup(relay_id: relay)?.is_paid ?? false)
if let meta = state.relay_metadata.lookup(relay_id: relay) {
Text(relay)
.background(
NavigationLink("", destination: RelayDetailView(state: state, relay: relay, nip11: meta)).opacity(0.0)
.disabled(showActionButtons)
)
Spacer()
Image(systemName: "info.circle")
.font(.system(size: 20, weight: .regular))
.foregroundColor(Color.accentColor)
} else {
Text(relay)
Spacer()
Image(systemName: "questionmark.circle")
.font(.system(size: 20, weight: .regular))
.foregroundColor(.gray)
}
}
}
.swipeActions {
if let privkey = state.keypair.privkey {
RemoveAction(privkey: privkey)
RemoveButton(privkey: privkey, showText: false)
.tint(.red)
}
}
.contextMenu {
CopyAction(relay: relay)
if let privkey = state.keypair.privkey {
RemoveAction(privkey: privkey)
RemoveButton(privkey: privkey, showText: true)
}
}
}
@@ -48,9 +69,9 @@ struct RelayView: View {
Label(NSLocalizedString("Copy", comment: "Button to copy a relay server address."), systemImage: "doc.on.doc")
}
}
func RemoveAction(privkey: String) -> some View {
Button {
func RemoveButton(privkey: String, showText: Bool) -> some View {
Button(action: {
guard let ev = state.contacts.event else {
return
}
@@ -62,15 +83,20 @@ struct RelayView: View {
process_contact_event(state: state, ev: new_ev)
state.pool.send(.event(new_ev))
} label: {
Label(NSLocalizedString("Delete", comment: "Button to delete a relay server that the user connects to."), systemImage: "trash")
}) {
if showText {
Text(NSLocalizedString("Disconnect", comment: "Button to disconnect from a relay server."))
}
Image(systemName: "minus.circle.fill")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.red)
.padding(.leading, 5)
}
.tint(.red)
}
}
struct RelayView_Previews: PreviewProvider {
static var previews: some View {
RelayView(state: test_damus_state(), relay: "wss://relay.damus.io")
RelayView(state: test_damus_state(), relay: "wss://relay.damus.io", showActionButtons: .constant(false))
}
}

View File

@@ -13,6 +13,7 @@ struct UserRelaysView: View {
let relays: [String]
@State var relay_state: [(String, Bool)]
@State private var showAddButton = false
init (state: DamusState, pubkey: String, relays: [String]) {
self.state = state
@@ -30,12 +31,25 @@ struct UserRelaysView: View {
var body: some View {
List(relay_state, id: \.0) { (r, add) in
RecommendedRelayView(damus: state, relay: r, add_button: add)
RecommendedRelayView(damus: state, relay: r, add_button: add, showActionButtons: $showAddButton)
}
.onReceive(handle_notify(.relays_changed)) { _ in
self.relay_state = UserRelaysView.make_relay_state(pool: state.pool, relays: self.relays)
}
.navigationBarTitle("Relays")
.toolbar{
if state.keypair.privkey != nil {
if showAddButton {
Button("Done") {
showAddButton.toggle()
}
} else {
Button("Show Add") {
showAddButton.toggle()
}
}
}
}
}
}