commit 6de7d7ae58e1bcb93d62a66e9b9a775a7765346d
parent e104de6431edc517660a48143f5e64e6d2b82643
Author: William Casarin <jb55@jb55.com>
Date: Thu, 9 Jun 2022 13:47:25 -0700
edit relays
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
7 files changed, 328 insertions(+), 35 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -77,6 +77,9 @@
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; };
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; };
+ 4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; };
+ 4CE4F9E1285287B800C00DD9 /* TextFieldAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9E0285287B800C00DD9 /* TextFieldAlert.swift */; };
+ 4CE4F9E328528C5200C00DD9 /* AddRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */; };
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DEE627F7A08100C66700 /* damusApp.swift */; };
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DEE827F7A08100C66700 /* ContentView.swift */; };
4CE6DEEB27F7A08200C66700 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4CE6DEEA27F7A08200C66700 /* Assets.xcassets */; };
@@ -185,6 +188,9 @@
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; };
4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
+ 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; };
+ 4CE4F9E0285287B800C00DD9 /* TextFieldAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldAlert.swift; sourceTree = "<group>"; };
+ 4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRelayView.swift; sourceTree = "<group>"; };
4CE6DEE327F7A08100C66700 /* damus.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = damus.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CE6DEE627F7A08100C66700 /* damusApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusApp.swift; sourceTree = "<group>"; };
4CE6DEE827F7A08100C66700 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -297,6 +303,8 @@
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */,
4C90BD17283A9EE5008EE7EF /* LoginView.swift */,
4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */,
+ 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */,
+ 4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
);
path = Views;
sourceTree = "<group>";
@@ -336,6 +344,14 @@
path = Util;
sourceTree = "<group>";
};
+ 4CE4F9DF285287A000C00DD9 /* Components */ = {
+ isa = PBXGroup;
+ children = (
+ 4CE4F9E0285287B800C00DD9 /* TextFieldAlert.swift */,
+ );
+ path = Components;
+ sourceTree = "<group>";
+ };
4CE6DEDA27F7A08100C66700 = {
isa = PBXGroup;
children = (
@@ -360,6 +376,7 @@
4CE6DEE527F7A08100C66700 /* damus */ = {
isa = PBXGroup;
children = (
+ 4CE4F9DF285287A000C00DD9 /* Components */,
4C7FF7D628233637009601DB /* Util */,
4C0A3F8D280F63FF000448DE /* Models */,
4C75EFAB28049CC80006080F /* Nostr */,
@@ -551,6 +568,7 @@
4C363A8A28236B57006E126D /* MentionView.swift in Sources */,
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */,
4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */,
+ 4CE4F9E1285287B800C00DD9 /* TextFieldAlert.swift in Sources */,
4C363AA828297703006E126D /* InsertSort.swift in Sources */,
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */,
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
@@ -571,10 +589,12 @@
4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */,
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */,
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
+ 4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */,
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */,
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
4C363A8428233689006E126D /* Parser.swift in Sources */,
+ 4CE4F9E328528C5200C00DD9 /* AddRelayView.swift in Sources */,
4C363A9A28283854006E126D /* Reply.swift in Sources */,
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */,
diff --git a/damus/Components/TextFieldAlert.swift b/damus/Components/TextFieldAlert.swift
@@ -0,0 +1,61 @@
+//
+// TextFieldAlert.swift
+// damus
+//
+// Created by William Casarin on 2022-06-09.
+//
+
+import SwiftUI
+
+struct TextFieldAlert<Presenting>: View where Presenting: View {
+ @Binding var isShowing: Bool
+ @Binding var text: String
+ let presenting: Presenting
+ let title: String
+
+ var body: some View {
+ GeometryReader { (deviceSize: GeometryProxy) in
+ ZStack {
+ self.presenting
+ .disabled(isShowing)
+ VStack {
+ Text(self.title)
+ TextField("Relay", text: self.$text)
+ Divider()
+ HStack {
+ Button(action: {
+ withAnimation {
+ self.isShowing.toggle()
+ }
+ }) {
+ Text("Dismiss")
+ }
+ }
+ }
+ .padding()
+ .background(Color.white)
+ .frame(
+ width: deviceSize.size.width*0.7,
+ height: deviceSize.size.height*0.7
+ )
+ .shadow(radius: 1)
+ .opacity(self.isShowing ? 1 : 0)
+ }
+ }
+ }
+
+}
+
+
+extension View {
+
+ func textFieldAlert(isShowing: Binding<Bool>,
+ text: Binding<String>,
+ title: String) -> some View {
+ TextFieldAlert(isShowing: isShowing,
+ text: text,
+ presenting: self,
+ title: title)
+ }
+
+}
diff --git a/damus/ContentView.swift b/damus/ContentView.swift
@@ -63,7 +63,7 @@ struct ContentView: View {
var LoadingContainer: some View {
VStack {
- HStack {
+ HStack(alignment: .center) {
Spacer()
if home.signal.signal != home.signal.max_signal {
@@ -71,6 +71,11 @@ struct ContentView: View {
.font(.callout)
.foregroundColor(.gray)
}
+
+ NavigationLink(destination: ConfigView(state: damus_state!)) {
+ Label("", systemImage: "gear")
+ }
+ .buttonStyle(PlainButtonStyle())
}
Spacer()
@@ -91,36 +96,32 @@ struct ContentView: View {
}
func MainContent(damus: DamusState) -> some View {
- NavigationView {
- VStack {
- NavigationLink(destination: MaybeProfileView, isActive: $profile_open) {
- EmptyView()
- }
- NavigationLink(destination: MaybeThreadView, isActive: $thread_open) {
- EmptyView()
- }
- NavigationLink(destination: MaybeSearchView, isActive: $search_open) {
- EmptyView()
- }
- switch selected_timeline {
- case .search:
- SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(pool: damus_state!.pool) )
-
- case .home:
- PostingTimelineView
-
- case .notifications:
- TimelineView(events: $home.notifications, damus: damus)
- .navigationTitle("Notifications")
+ VStack {
+ NavigationLink(destination: MaybeProfileView, isActive: $profile_open) {
+ EmptyView()
+ }
+ NavigationLink(destination: MaybeThreadView, isActive: $thread_open) {
+ EmptyView()
+ }
+ NavigationLink(destination: MaybeSearchView, isActive: $search_open) {
+ EmptyView()
+ }
+ switch selected_timeline {
+ case .search:
+ SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(pool: damus_state!.pool) )
- case .none:
- EmptyView()
- }
+ case .home:
+ PostingTimelineView
+
+ case .notifications:
+ TimelineView(events: $home.notifications, damus: damus)
+ .navigationTitle("Notifications")
+
+ case .none:
+ EmptyView()
}
- .navigationBarTitle("Damus", displayMode: .inline)
-
}
- .navigationViewStyle(.stack)
+ .navigationBarTitle("Damus", displayMode: .inline)
}
var MaybeSearchView: some View {
@@ -159,12 +160,13 @@ struct ContentView: View {
var body: some View {
VStack {
if let damus = self.damus_state {
- ZStack {
+ NavigationView {
MainContent(damus: damus)
- .padding([.bottom], -8.0)
-
- LoadingContainer
+ .toolbar {
+ LoadingContainer
+ }
}
+ .navigationViewStyle(.stack)
}
TabBar(new_notifications: $home.new_notifications, selected: $selected_timeline, action: switch_timeline)
diff --git a/damus/Models/Contacts.swift b/damus/Models/Contacts.swift
@@ -138,14 +138,52 @@ func decode_json_relays(_ content: String) -> [String: RelayInfo]? {
return decode_json(content)
}
-/*
+func remove_relay(ev: NostrEvent, privkey: String, relay: String) -> NostrEvent? {
+ let damus_relay = RelayDescriptor(url: URL(string: "wss://relay.damus.io")!, info: .rw)
+
+ var relays = ensure_relay_info(relays: [damus_relay], content: ev.content)
+ guard relays.index(forKey: relay) != nil else {
+ return nil
+ }
+
+ relays.removeValue(forKey: relay)
+
+ guard let content = encode_json(relays) else {
+ return nil
+ }
+
+ let new_ev = NostrEvent(content: content, pubkey: ev.pubkey, kind: 3, tags: ev.tags)
+ new_ev.calculate_id()
+ new_ev.sign(privkey: privkey)
+ return new_ev
+}
+
+func add_relay(ev: NostrEvent, privkey: String, relay: String, info: RelayInfo) -> NostrEvent? {
+ let damus_relay = RelayDescriptor(url: URL(string: "wss://relay.damus.io")!, info: .rw)
+
+ var relays = ensure_relay_info(relays: [damus_relay], content: ev.content)
+ guard relays.index(forKey: relay) == nil else {
+ return nil
+ }
+
+ relays[relay] = info
+
+ guard let content = encode_json(relays) else {
+ return nil
+ }
+
+ let new_ev = NostrEvent(content: content, pubkey: ev.pubkey, kind: 3, tags: ev.tags)
+ new_ev.calculate_id()
+ new_ev.sign(privkey: privkey)
+ return new_ev
+}
+
func ensure_relay_info(relays: [RelayDescriptor], content: String) -> [String: RelayInfo] {
guard let relay_info = decode_json_relays(content) else {
return make_contact_relays(relays)
}
return relay_info
}
- */
func follow_with_existing_contacts(our_pubkey: String, our_contacts: NostrEvent, follow: ReferencedId) -> NostrEvent? {
// don't update if we're already following
diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift
@@ -165,7 +165,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible {
public var is_local: Bool {
return (self.flags & 1) != 0
}
-
+
init(content: String, pubkey: String, kind: Int = 1, tags: [[String]] = []) {
self.id = ""
self.sig = ""
@@ -176,6 +176,17 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible {
self.tags = tags
self.created_at = Int64(Date().timeIntervalSince1970)
}
+
+ init(from: NostrEvent) {
+ self.id = from.id
+ self.sig = from.sig
+
+ self.content = from.content
+ self.pubkey = from.pubkey
+ self.kind = from.kind
+ self.tags = from.tags
+ self.created_at = from.created_at
+ }
func calculate_id() {
self.id = calculate_event_id(ev: self)
diff --git a/damus/Views/AddRelayView.swift b/damus/Views/AddRelayView.swift
@@ -0,0 +1,55 @@
+//
+// AddRelayView.swift
+// damus
+//
+// Created by William Casarin on 2022-06-09.
+//
+
+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("Add Relay") {
+ TextField("wss://some.relay.com", text: $relay)
+ .textInputAutocapitalization(.never)
+ }
+ }
+
+ VStack {
+ HStack {
+ Button("Cancel") {
+ show_add_relay = false
+ action(nil)
+ }
+ .contentShape(Rectangle())
+
+ Spacer()
+
+ Button("Add") {
+ show_add_relay = false
+ action(relay)
+ }
+ .buttonStyle(.borderedProminent)
+ .contentShape(Rectangle())
+ }
+ .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 })
+ }
+}
diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift
@@ -0,0 +1,106 @@
+//
+// ConfigView.swift
+// damus
+//
+// Created by William Casarin on 2022-06-09.
+//
+
+import SwiftUI
+
+struct ConfigView: View {
+ let state: DamusState
+ @Environment(\.dismiss) var dismiss
+ @State var show_add_relay: Bool = false
+ @State var new_relay: String = ""
+
+ func Relay(_ ev: NostrEvent, relay: String) -> some View {
+ return Text(relay)
+ .swipeActions {
+ if let privkey = state.keypair.privkey {
+ Button {
+ guard let new_ev = remove_relay( ev: ev, privkey: privkey, relay: relay) else {
+ return
+ }
+
+ state.contacts.event = new_ev
+ state.pool.send(.event(new_ev))
+ } label: {
+ Label("Delete", systemImage: "trash")
+ }
+ .tint(.red)
+ }
+ }
+ }
+
+ var body: some View {
+ ZStack(alignment: .leading) {
+ Form {
+ if let ev = state.contacts.event {
+ Section("Relays") {
+ if let relays = decode_json_relays(ev.content) {
+ List(Array(relays.keys.sorted()), id: \.self) { relay in
+ Relay(ev, relay: relay)
+ }
+ }
+
+ }
+ }
+ }
+
+ VStack {
+ HStack {
+ Spacer()
+
+ Button(action: { show_add_relay = true }) {
+ Label("", systemImage: "plus")
+ .foregroundColor(.accentColor)
+ .padding()
+ }
+ }
+
+ Spacer()
+ }
+ }
+ .navigationTitle("Settings")
+ .navigationBarTitleDisplayMode(.large)
+ .sheet(isPresented: $show_add_relay) {
+ AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { _ in
+ 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, relay: new_relay, info: info) else {
+ return
+ }
+
+ state.contacts.event = new_ev
+ state.pool.send(.event(new_ev))
+ }
+ }
+ .onReceive(handle_notify(.switched_timeline)) { _ in
+ dismiss()
+ }
+ }
+}
+
+struct ConfigView_Previews: PreviewProvider {
+ static var previews: some View {
+ ConfigView(state: test_damus_state())
+ }
+}