diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 2a30c20..df0ab18 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; }; 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; }; 31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; }; + 31D2E84D295225C3006D67F8 /* EditAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E84C295225C3006D67F8 /* EditAccountView.swift */; }; + 31FE49F629535D3700D6DEA1 /* DamusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31FE49F529535D3700D6DEA1 /* DamusViewModel.swift */; }; + 31FE49F92953731D00D6DEA1 /* EditAccountButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31FE49F82953731D00D6DEA1 /* EditAccountButton.swift */; }; 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; }; 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; }; 4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; }; @@ -27,7 +30,7 @@ 4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F372871EDE300040376 /* DirectMessageModel.swift */; }; 4C285C8228385570008A31F1 /* CarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8128385570008A31F1 /* CarouselView.swift */; }; 4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8328385690008A31F1 /* CreateAccountView.swift */; }; - 4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C85283892E7008A31F1 /* CreateAccountModel.swift */; }; + 4C285C86283892E7008A31F1 /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C85283892E7008A31F1 /* AccountModel.swift */; }; 4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */; }; 4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8B28398BC6008A31F1 /* Keys.swift */; }; 4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */; }; @@ -152,6 +155,9 @@ 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = ""; }; 3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; }; 31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = ""; }; + 31D2E84C295225C3006D67F8 /* EditAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAccountView.swift; sourceTree = ""; }; + 31FE49F529535D3700D6DEA1 /* DamusViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusViewModel.swift; sourceTree = ""; }; + 31FE49F82953731D00D6DEA1 /* EditAccountButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAccountButton.swift; sourceTree = ""; }; 4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = ""; }; 4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = ""; }; 4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = ""; }; @@ -171,7 +177,7 @@ 4C216F372871EDE300040376 /* DirectMessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessageModel.swift; sourceTree = ""; }; 4C285C8128385570008A31F1 /* CarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselView.swift; sourceTree = ""; }; 4C285C8328385690008A31F1 /* CreateAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateAccountView.swift; sourceTree = ""; }; - 4C285C85283892E7008A31F1 /* CreateAccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateAccountModel.swift; sourceTree = ""; }; + 4C285C85283892E7008A31F1 /* AccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountModel.swift; sourceTree = ""; }; 4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePictureSelector.swift; sourceTree = ""; }; 4C285C8B28398BC6008A31F1 /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = ""; }; 4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveKeysView.swift; sourceTree = ""; }; @@ -344,6 +350,15 @@ path = "Empty Views"; sourceTree = ""; }; + 31FE49F72953730900D6DEA1 /* Account */ = { + isa = PBXGroup; + children = ( + 31D2E84C295225C3006D67F8 /* EditAccountView.swift */, + 31FE49F82953731D00D6DEA1 /* EditAccountButton.swift */, + ); + path = Account; + sourceTree = ""; + }; 4C06670728FDE62900038D2A /* damus-c */ = { isa = PBXGroup; children = ( @@ -416,7 +431,7 @@ 4C363A9B282838B9006E126D /* EventRef.swift */, 4C363AA328296DEE006E126D /* SearchModel.swift */, 4C3AC79A28306D7B00E1F516 /* Contacts.swift */, - 4C285C85283892E7008A31F1 /* CreateAccountModel.swift */, + 4C285C85283892E7008A31F1 /* AccountModel.swift */, 4C63334F283D40E500B1C9C3 /* HomeModel.swift */, 4C633351283D419F00B1C9C3 /* SignalModel.swift */, 4C5F9113283D694D0052CD1C /* FollowTarget.swift */, @@ -442,6 +457,7 @@ 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */, 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */, 4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */, + 31FE49F72953730900D6DEA1 /* Account */, 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */, 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */, 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */, @@ -559,6 +575,7 @@ 4C75EFA72804823E0006080F /* Info.plist */, 4C75EFA227FA576C0006080F /* Views */, 4CE6DEE627F7A08100C66700 /* damusApp.swift */, + 31FE49F529535D3700D6DEA1 /* DamusViewModel.swift */, 4CE6DEE827F7A08100C66700 /* ContentView.swift */, 4CE6DEEA27F7A08200C66700 /* Assets.xcassets */, 4CE6DEEC27F7A08200C66700 /* Preview Content */, @@ -748,12 +765,13 @@ 4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */, 4C363A8A28236B57006E126D /* MentionView.swift in Sources */, 4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */, + 31D2E84D295225C3006D67F8 /* EditAccountView.swift in Sources */, 4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */, 4C216F34286F5ACD00040376 /* DMView.swift in Sources */, 4C3EA64428FF558100C48A62 /* sha256.c in Sources */, 4CE4F9E1285287B800C00DD9 /* TextFieldAlert.swift in Sources */, 4C363AA828297703006E126D /* InsertSort.swift in Sources */, - 4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */, + 4C285C86283892E7008A31F1 /* AccountModel.swift in Sources */, 4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */, 4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */, 4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */, @@ -803,9 +821,11 @@ 4C3EA67528FF7A5A00C48A62 /* take.c in Sources */, 4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */, 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */, + 31FE49F92953731D00D6DEA1 /* EditAccountButton.swift in Sources */, 4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */, 4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */, 4C633352283D419F00B1C9C3 /* SignalModel.swift in Sources */, + 31FE49F629535D3700D6DEA1 /* DamusViewModel.swift in Sources */, 4C363A94282704FA006E126D /* Post.swift in Sources */, 4C216F32286E388800040376 /* DMChatView.swift in Sources */, 4C3EA67928FF7ABF00C48A62 /* list.c in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 9b05b38..989e44a 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -54,20 +54,7 @@ struct ContentView: View { return keypair.privkey } - @State var status: String = "Not connected" - @State var active_sheet: Sheets? = nil - @State var damus_state: DamusState? = nil - @State var selected_timeline: Timeline? = .home - @State var is_thread_open: Bool = false - @State var is_profile_open: Bool = false - @State var event: NostrEvent? = nil - @State var active_profile: String? = nil - @State var active_search: NostrFilter? = nil - @State var active_event_id: String? = nil - @State var profile_open: Bool = false - @State var thread_open: Bool = false - @State var search_open: Bool = false - @State var filter_state : FilterState = .posts_and_replies + @EnvironmentObject var viewModel: DamusViewModel @StateObject var home: HomeModel = HomeModel() // connect retry timer @@ -80,12 +67,12 @@ struct ContentView: View { var PostingTimelineView: some View { VStack{ ZStack { - if let damus = self.damus_state { + if let damus = viewModel.damus_state { TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event) } if privkey != nil { PostButtonContainer { - self.active_sheet = .post + viewModel.active_sheet = .post } } } @@ -104,7 +91,7 @@ struct ContentView: View { var FiltersView: some View { VStack{ - Picker("Filter State", selection: $filter_state) { + Picker("Filter State", selection: $viewModel.filter_state) { Text("Posts").tag(FilterState.posts) Text("Posts & Replies").tag(FilterState.posts_and_replies) } @@ -113,7 +100,7 @@ struct ContentView: View { } func filter_event(_ ev: NostrEvent) -> Bool { - if self.filter_state == .posts { + if viewModel.filter_state == .posts { return !ev.is_reply(nil) } @@ -122,18 +109,18 @@ struct ContentView: View { func MainContent(damus: DamusState) -> some View { VStack { - NavigationLink(destination: MaybeProfileView, isActive: $profile_open) { + NavigationLink(destination: MaybeProfileView, isActive: $viewModel.profile_open) { EmptyView() } - NavigationLink(destination: MaybeThreadView, isActive: $thread_open) { + NavigationLink(destination: MaybeThreadView, isActive: $viewModel.thread_open) { EmptyView() } - NavigationLink(destination: MaybeSearchView, isActive: $search_open) { + NavigationLink(destination: MaybeSearchView, isActive: $viewModel.search_open) { EmptyView() } - switch selected_timeline { + switch viewModel.selected_timeline { case .search: - SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(damus_state: damus_state!)) + SearchHomeView(damus_state: viewModel.damus_state!, model: SearchHomeModel(damus_state: viewModel.damus_state!)) case .home: PostingTimelineView @@ -143,20 +130,20 @@ struct ContentView: View { .navigationTitle("Notifications") case .dms: - DirectMessagesView(damus_state: damus_state!) + DirectMessagesView(damus_state: viewModel.damus_state!) .environmentObject(home.dms) case .none: EmptyView() } } - .navigationBarTitle(selected_timeline == .home ? "Home" : "Global", displayMode: .inline) + .navigationBarTitle(viewModel.selected_timeline == .home ? "Home" : "Global", displayMode: .inline) } var MaybeSearchView: some View { Group { - if let search = self.active_search { - SearchView(appstate: damus_state!, search: SearchModel(pool: damus_state!.pool, search: search)) + if let search = viewModel.active_search { + SearchView(appstate: viewModel.damus_state!, search: SearchModel(pool: viewModel.damus_state!.pool, search: search)) } else { EmptyView() } @@ -165,9 +152,9 @@ struct ContentView: View { var MaybeThreadView: some View { Group { - if let evid = self.active_event_id { - let thread_model = ThreadModel(evid: evid, damus_state: damus_state!) - ThreadView(thread: thread_model, damus: damus_state!, is_chatroom: false) + if let evid = viewModel.active_event_id { + let thread_model = ThreadModel(evid: evid, damus_state: viewModel.damus_state!) + ThreadView(thread: thread_model, damus: viewModel.damus_state!, is_chatroom: false) } else { EmptyView() } @@ -176,10 +163,10 @@ struct ContentView: View { var MaybeProfileView: some View { Group { - if let pk = self.active_profile { - let profile_model = ProfileModel(pubkey: pk, damus: damus_state!) - let followers = FollowersModel(damus_state: damus_state!, target: pk) - ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers) + if let pk = viewModel.active_profile { + let profile_model = ProfileModel(pubkey: pk, damus: viewModel.damus_state!) + let followers = FollowersModel(damus_state: viewModel.damus_state!, target: pk) + ProfileView(damus_state: viewModel.damus_state!, profile: profile_model, followers: followers) } else { EmptyView() } @@ -188,20 +175,20 @@ struct ContentView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - if let damus = self.damus_state { + if let damus = viewModel.damus_state { NavigationView { MainContent(damus: damus) .toolbar { ToolbarItem(placement: .navigationBarLeading) { - let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!) - let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey) - let prof_dest = ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers_model) + let profile_model = ProfileModel(pubkey: viewModel.damus_state!.pubkey, damus: viewModel.damus_state!) + let followers_model = FollowersModel(damus_state: viewModel.damus_state!, target: viewModel.damus_state!.pubkey) + let prof_dest = ProfileView(damus_state: viewModel.damus_state!, profile: profile_model, followers: followers_model) NavigationLink(destination: prof_dest) { /// Verify that the user has a profile picture, if not display a generic SF Symbol /// (Resolves an in-app error where ``Robohash`` pictures are not generated so the button dissapears - if let picture = damus_state?.profiles.lookup(id: pubkey)?.picture { - ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, picture: picture) + if let picture = viewModel.damus_state?.profiles.lookup(id: pubkey)?.picture { + ProfilePicView(pubkey: viewModel.damus_state!.pubkey, size: 32, highlight: .none, profiles: viewModel.damus_state!.profiles, picture: picture) } else { Image(systemName: "person.fill") } @@ -217,7 +204,7 @@ struct ContentView: View { .foregroundColor(.gray) } - NavigationLink(destination: ConfigView(state: damus_state!)) { + NavigationLink(destination: ConfigView(state: viewModel.damus_state!)) { Label("", systemImage: "gear") } .buttonStyle(PlainButtonStyle()) @@ -228,19 +215,19 @@ struct ContentView: View { .navigationViewStyle(.stack) } - TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline) + TabBar(new_events: $home.new_events, selected: $viewModel.selected_timeline, action: switch_timeline) } .onAppear() { self.connect() //KingfisherManager.shared.cache.clearDiskCache() setup_notifications() } - .sheet(item: $active_sheet) { item in + .sheet(item: $viewModel.active_sheet) { item in switch item { case .post: PostView(replying_to: nil, references: []) case .reply(let event): - ReplyView(replying_to: event, damus: damus_state!) + ReplyView(replying_to: event, damus: viewModel.damus_state!) } } .onOpenURL { url in @@ -251,15 +238,15 @@ struct ContentView: View { switch link { case .ref(let ref): if ref.key == "p" { - active_profile = ref.ref_id - profile_open = true + viewModel.active_profile = ref.ref_id + viewModel.profile_open = true } else if ref.key == "e" { - active_event_id = ref.ref_id - thread_open = true + viewModel.active_event_id = ref.ref_id + viewModel.thread_open = true } case .filter(let filt): - active_search = filt - search_open = true + viewModel.active_search = filt + viewModel.search_open = true break // TODO: handle filter searches? } @@ -272,7 +259,7 @@ struct ContentView: View { let ev = notif.object as! NostrEvent let boost = make_boost_event(pubkey: pubkey, privkey: privkey, boosted: ev) - self.damus_state?.pool.send(.event(boost)) + viewModel.damus_state?.pool.send(.event(boost)) } .onReceive(handle_notify(.open_thread)) { obj in //let ev = obj.object as! NostrEvent @@ -281,20 +268,20 @@ struct ContentView: View { } .onReceive(handle_notify(.reply)) { notif in let ev = notif.object as! NostrEvent - self.active_sheet = .reply(ev) + viewModel.active_sheet = .reply(ev) } .onReceive(handle_notify(.like)) { like in } .onReceive(handle_notify(.broadcast_event)) { obj in let ev = obj.object as! NostrEvent - self.damus_state?.pool.send(.event(ev)) + viewModel.damus_state?.pool.send(.event(ev)) } .onReceive(handle_notify(.unfollow)) { notif in guard let privkey = self.privkey else { return } - guard let damus = self.damus_state else { + guard let damus = viewModel.damus_state else { return } @@ -319,7 +306,7 @@ struct ContentView: View { } let fnotify = notif.object as! FollowTarget - guard let damus = self.damus_state else { + guard let damus = viewModel.damus_state else { return } @@ -330,7 +317,7 @@ struct ContentView: View { follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) { notify(.followed, fnotify.pubkey) - damus_state?.contacts.event = ev + viewModel.damus_state?.contacts.event = ev switch fnotify { case .pubkey(let pk): @@ -350,26 +337,26 @@ struct ContentView: View { case .post(let post): print("post \(post.content)") let new_ev = post_to_event(post: post, privkey: privkey, pubkey: pubkey) - self.damus_state?.pool.send(.event(new_ev)) + viewModel.damus_state?.pool.send(.event(new_ev)) case .cancel: - active_sheet = nil + viewModel.active_sheet = nil print("post cancelled") } } .onReceive(timer) { n in - self.damus_state?.pool.connect_to_disconnected() + viewModel.damus_state?.pool.connect_to_disconnected() } } func switch_timeline(_ timeline: Timeline) { NotificationCenter.default.post(name: .switched_timeline, object: timeline) - if timeline == self.selected_timeline { + if timeline == viewModel.selected_timeline { NotificationCenter.default.post(name: .scroll_to_top, object: nil) return } - self.selected_timeline = timeline + viewModel.selected_timeline = timeline //NotificationCenter.default.post(name: .switched_timeline, object: timeline) //self.selected_timeline = timeline } @@ -394,7 +381,7 @@ struct ContentView: View { pool.register_handler(sub_id: sub_id, handler: home.handle_event) - self.damus_state = DamusState(pool: pool, keypair: keypair, + viewModel.damus_state = DamusState(pool: pool, keypair: keypair, likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(), @@ -402,7 +389,7 @@ struct ContentView: View { profiles: Profiles(), dms: home.dms ) - home.damus_state = self.damus_state! + home.damus_state = viewModel.damus_state! pool.connect() } diff --git a/damus/DamusViewModel.swift b/damus/DamusViewModel.swift new file mode 100644 index 0000000..71ba641 --- /dev/null +++ b/damus/DamusViewModel.swift @@ -0,0 +1,25 @@ +// +// DamusViewModel.swift +// damus +// +// Created by Sam DuBois on 12/21/22. +// + +import SwiftUI + +class DamusViewModel: ObservableObject { + @Published var status: String = "Not connected" + @Published var active_sheet: Sheets? = nil + @Published var damus_state: DamusState? = nil + @Published var selected_timeline: Timeline? = .home + @Published var is_thread_open: Bool = false + @Published var is_profile_open: Bool = false + @Published var event: NostrEvent? = nil + @Published var active_profile: String? = nil + @Published var active_search: NostrFilter? = nil + @Published var active_event_id: String? = nil + @Published var profile_open: Bool = false + @Published var thread_open: Bool = false + @Published var search_open: Bool = false + @Published var filter_state : FilterState = .posts_and_replies +} diff --git a/damus/Models/CreateAccountModel.swift b/damus/Models/AccountModel.swift similarity index 76% rename from damus/Models/CreateAccountModel.swift rename to damus/Models/AccountModel.swift index cff2586..0dcb58b 100644 --- a/damus/Models/CreateAccountModel.swift +++ b/damus/Models/AccountModel.swift @@ -8,12 +8,13 @@ import Foundation -class CreateAccountModel: ObservableObject { +class AccountModel: ObservableObject { @Published var real_name: String = "" @Published var nick_name: String = "" @Published var about: String = "" @Published var pubkey: String = "" @Published var privkey: String = "" + @Published var picture: String = "" var pubkey_bech32: String { return bech32_pubkey(self.pubkey) ?? "" @@ -49,4 +50,13 @@ class CreateAccountModel: ObservableObject { self.nick_name = nick self.about = about } + + init(keys: Keypair, real: String, user: String, about: String, picture: String) { + self.pubkey = keys.pubkey + self.privkey = keys.privkey ?? "" + self.real_name = real + self.nick_name = user + self.about = about + self.picture = picture + } } diff --git a/damus/Nostr/NostrMetadata.swift b/damus/Nostr/NostrMetadata.swift index faf5963..8cd3881 100644 --- a/damus/Nostr/NostrMetadata.swift +++ b/damus/Nostr/NostrMetadata.swift @@ -13,8 +13,9 @@ struct NostrMetadata: Codable { let name: String? let about: String? let website: String? + let picture: String? } -func create_account_to_metadata(_ model: CreateAccountModel) -> NostrMetadata { - return NostrMetadata(display_name: model.real_name, name: model.nick_name, about: model.about, website: nil) +func account_to_metadata(_ model: AccountModel) -> NostrMetadata { + return NostrMetadata(display_name: model.real_name, name: model.nick_name, about: model.about, website: model.picture, picture: model.picture) } diff --git a/damus/Views/Account/EditAccountButton.swift b/damus/Views/Account/EditAccountButton.swift new file mode 100644 index 0000000..9e90d59 --- /dev/null +++ b/damus/Views/Account/EditAccountButton.swift @@ -0,0 +1,41 @@ +// +// EditAccountButton.swift +// damus +// +// Created by Sam DuBois on 12/21/22. +// + +import SwiftUI + +struct EditAccountButton: View { + + @EnvironmentObject var viewModel: DamusViewModel + + @State private var presentEditAccountView: Bool = false + + var body: some View { + Button { + presentEditAccountView.toggle() + } label: { + Text("Edit") + .padding(.horizontal, 20) + .padding(.vertical, 7) + .font(.caption.weight(.bold)) + .foregroundColor(.white) + .background(viewModel.damus_state != nil ? hex_to_rgb(viewModel.damus_state!.pubkey) : .black) + .cornerRadius(20) + } + .sheet(isPresented: $presentEditAccountView, content: { + EditAccountView() + }) + + } +} + + +struct EditAccountButton_Previews: PreviewProvider { + static var previews: some View { + EditAccountButton() + .environmentObject(DamusViewModel()) + } +} diff --git a/damus/Views/Account/EditAccountView.swift b/damus/Views/Account/EditAccountView.swift new file mode 100644 index 0000000..d1a857c --- /dev/null +++ b/damus/Views/Account/EditAccountView.swift @@ -0,0 +1,122 @@ +// +// EditAccountView.swift +// damus +// +// Created by Sam DuBois on 12/20/22. +// + +import SwiftUI + +struct EditAccountView: View { + + @EnvironmentObject var viewModel: DamusViewModel + @Environment(\.dismiss) var dismiss + + @State var account: AccountModel = AccountModel() + @State var loading: Bool = true + @State var error: Error? + + var body: some View { + NavigationView { + if let state = viewModel.damus_state, !loading { + Form { + HStack { + Spacer() + ProfilePicView(pubkey: state.pubkey, size: 100, highlight: .main, profiles: state.profiles) + Spacer() + } + .listRowBackground(Color.clear) + + Section(header: Text("Photo")) { + TextField("Photo URL", text: $account.picture) + } + + Section(header: Text("Details")) { + TextField("Username", text: $account.real_name) + TextField("Personal Name", text: $account.nick_name) + TextEditor(text: $account.about) + .frame(height: 150) + + } + + Section(header: Text("Keys")) { + TextField("Public Key", text: .constant(account.keypair.pubkey)) + TextField("Secret Key", text: .constant(account.keypair.privkey ?? "")) + } + } + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button { + self.loading = true + viewModel.damus_state!.pool.register_handler(sub_id: "editaccount", handler: handle_event) + viewModel.damus_state!.pool.connect() + } label: { + Text("Save") + } + } + ToolbarItem(placement: .cancellationAction) { + Button { + dismiss() + } label: { + Text("Cancel") + } + } + } + } else { + ProgressView() + } + } + .onAppear { + if let state = viewModel.damus_state { + + guard let profile = state.profiles.lookup(id: state.pubkey) else { return } + + account = AccountModel(keys: state.keypair, real: profile.display_name ?? "", user: profile.name ?? "", about: profile.about ?? "", picture: profile.picture ?? "") + + loading = false + } + } + } + + func handle_event(relay: String, ev: NostrConnectionEvent) { + switch ev { + case .ws_event(let wsev): + switch wsev { + case .connected: + let metadata = account_to_metadata(account) + let m_metadata_ev = make_metadata_event(keypair: account.keypair, metadata: metadata) + + if let state = viewModel.damus_state { + if let metadata_ev = m_metadata_ev { + state.pool.send(.event(metadata_ev)) + } + } + + case .error(let error): + self.loading = false + print(error) + default: + dismiss() + } + case .nostr_event(let resp): + switch resp { + case .notice(let msg): + // TODO handle message +// self.loading = false +// self.error = msg + print(msg) + case .event: + print("event in account edit request?") + case .eose: + break + } + } + } +} + +struct EditAccountView_Previews: PreviewProvider { + static var previews: some View { + EditAccountView() + .environmentObject(DamusViewModel()) + } +} diff --git a/damus/Views/CreateAccountView.swift b/damus/Views/CreateAccountView.swift index 50c7326..55d762d 100644 --- a/damus/Views/CreateAccountView.swift +++ b/damus/Views/CreateAccountView.swift @@ -8,7 +8,7 @@ import SwiftUI struct CreateAccountView: View { - @StateObject var account: CreateAccountModel = CreateAccountModel() + @StateObject var account: AccountModel = AccountModel() @State var is_light: Bool = false @State var is_done: Bool = false @@ -118,7 +118,7 @@ extension View { struct CreateAccountView_Previews: PreviewProvider { static var previews: some View { - let model = CreateAccountModel(real: "", nick: "jb55", about: "") + let model = AccountModel(real: "", nick: "jb55", about: "") return CreateAccountView(account: model) } } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index b924f2e..d596d4d 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -127,7 +127,11 @@ struct ProfileView: View { DMButton - FollowButtonView(target: profile.get_follow_target(), follow_state: damus_state.contacts.follow_state(profile.pubkey)) + if damus_state.pubkey == profile.pubkey { + EditAccountButton() + } else { + FollowButtonView(target: profile.get_follow_target(), follow_state: damus_state.contacts.follow_state(profile.pubkey)) + } } ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts) diff --git a/damus/Views/SaveKeysView.swift b/damus/Views/SaveKeysView.swift index 70d26c0..a8c5a07 100644 --- a/damus/Views/SaveKeysView.swift +++ b/damus/Views/SaveKeysView.swift @@ -8,7 +8,7 @@ import SwiftUI struct SaveKeysView: View { - let account: CreateAccountModel + let account: AccountModel let pool: RelayPool = RelayPool() @State var is_done: Bool = false @State var pub_copied: Bool = false @@ -79,7 +79,7 @@ struct SaveKeysView: View { .navigationBarItems(leading: BackNav()) } - func complete_account_creation(_ account: CreateAccountModel) { + func complete_account_creation(_ account: AccountModel) { for relay in BOOTSTRAP_RELAYS { add_rw_relay(self.pool, relay) } @@ -96,7 +96,7 @@ struct SaveKeysView: View { case .ws_event(let wsev): switch wsev { case .connected: - let metadata = create_account_to_metadata(account) + let metadata = account_to_metadata(account) let m_metadata_ev = make_metadata_event(keypair: account.keypair, metadata: metadata) let m_contacts_ev = make_first_contact_event(keypair: account.keypair) @@ -175,7 +175,7 @@ struct SaveKeyView: View { struct SaveKeysView_Previews: PreviewProvider { static var previews: some View { - let model = CreateAccountModel(real: "William", nick: "jb55", about: "I'm me") + let model = AccountModel(real: "William", nick: "jb55", about: "I'm me") SaveKeysView(account: model) } } diff --git a/damus/damusApp.swift b/damus/damusApp.swift index d23a860..c43cc4e 100644 --- a/damus/damusApp.swift +++ b/damus/damusApp.swift @@ -23,10 +23,13 @@ struct MainView: View { @State var needs_setup = false; @State var keypair: Keypair? = nil; + @ObservedObject var viewModel: DamusViewModel = DamusViewModel() + var body: some View { Group { if let kp = keypair, !needs_setup { ContentView(keypair: kp) + .environmentObject(viewModel) } else { SetupView() .onReceive(handle_notify(.login)) { notif in