Browse Source

Removed the app viewModel. Need to find a better structure for the future to fix this issue

profile-edit
Sam DuBois 2 years ago
parent
commit
3a1d4b1c39
  1. 8
      damus.xcodeproj/project.pbxproj
  2. 261
      damus/ContentView.swift
  3. 162
      damus/DamusViewModel.swift
  4. 2
      damus/Views/TimelineView.swift
  5. 3
      damus/damusApp.swift

8
damus.xcodeproj/project.pbxproj

@ -7,10 +7,9 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
3169CAEB294FCABA00EE4006 /* Shimmer in Frameworks */ = {isa = PBXBuildFile; productRef = 3169CAEA294FCABA00EE4006 /* Shimmer */; }; 3169CAEB294FCABA00EE4006 /* Shimmer in Frameworks */ = {isa = PBXBuildFile; productRef = 3169CAEA294FCABA00EE4006 /* Shimmer */; };
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; }; 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
3169CAEF294FD4C400EE4006 /* DamusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEE294FD4C400EE4006 /* DamusViewModel.swift */; };
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; }; 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; }; 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; }; 4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; };
@ -150,9 +149,8 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; };
3169CAEE294FD4C400EE4006 /* DamusViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusViewModel.swift; sourceTree = "<group>"; };
3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; }; 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; };
3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; };
4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; }; 4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; };
4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; }; 4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; };
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = "<group>"; }; 4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = "<group>"; };
@ -560,7 +558,6 @@
4C75EFA72804823E0006080F /* Info.plist */, 4C75EFA72804823E0006080F /* Info.plist */,
4C75EFA227FA576C0006080F /* Views */, 4C75EFA227FA576C0006080F /* Views */,
4CE6DEE627F7A08100C66700 /* damusApp.swift */, 4CE6DEE627F7A08100C66700 /* damusApp.swift */,
3169CAEE294FD4C400EE4006 /* DamusViewModel.swift */,
4CE6DEE827F7A08100C66700 /* ContentView.swift */, 4CE6DEE827F7A08100C66700 /* ContentView.swift */,
4CE6DEEA27F7A08200C66700 /* Assets.xcassets */, 4CE6DEEA27F7A08200C66700 /* Assets.xcassets */,
4CE6DEEC27F7A08200C66700 /* Preview Content */, 4CE6DEEC27F7A08200C66700 /* Preview Content */,
@ -750,7 +747,6 @@
files = ( files = (
4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */, 4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */,
4C363A8A28236B57006E126D /* MentionView.swift in Sources */, 4C363A8A28236B57006E126D /* MentionView.swift in Sources */,
3169CAEF294FD4C400EE4006 /* DamusViewModel.swift in Sources */,
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */, 4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */,
4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */, 4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */,
4C216F34286F5ACD00040376 /* DMView.swift in Sources */, 4C216F34286F5ACD00040376 /* DMView.swift in Sources */,

261
damus/ContentView.swift

@ -9,24 +9,83 @@ import SwiftUI
import Starscream import Starscream
import Kingfisher import Kingfisher
var BOOTSTRAP_RELAYS = [
"wss://relay.damus.io",
"wss://nostr-relay.wlvs.space",
"wss://nostr.fmt.wiz.biz",
"wss://nostr.oxtr.dev",
]
struct TimestampedProfile {
let profile: Profile
let timestamp: Int64
}
enum Sheets: Identifiable {
case post
case reply(NostrEvent)
var id: String {
switch self {
case .post: return "post"
case .reply(let ev): return "reply-" + ev.id
}
}
}
enum ThreadState {
case event_details
case chatroom
}
enum FilterState : Int {
case posts_and_replies = 1
case posts = 0
}
struct ContentView: View { struct ContentView: View {
let keypair: Keypair
var pubkey: String {
return keypair.pubkey
}
var privkey: String? {
return keypair.privkey
}
@EnvironmentObject var viewModel: DamusViewModel @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
@StateObject var home: HomeModel = HomeModel()
// connect retry timer // connect retry timer
let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect() let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect()
let sub_id = UUID().description
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
var PostingTimelineView: some View { var PostingTimelineView: some View {
VStack{ VStack{
ZStack { ZStack {
if let damus = viewModel.state { if let damus = self.damus_state {
TimelineView(events: $viewModel.home.events, loading: $viewModel.home.loading, damus: damus, show_friend_icon: false, filter: filter_event) TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event)
} }
if viewModel.privkey != nil { if privkey != nil {
PostButtonContainer { PostButtonContainer {
viewModel.active_sheet = .post self.active_sheet = .post
} }
} }
} }
@ -45,7 +104,7 @@ struct ContentView: View {
var FiltersView: some View { var FiltersView: some View {
VStack{ VStack{
Picker("Filter State", selection: $viewModel.filter_state) { Picker("Filter State", selection: $filter_state) {
Text("Posts").tag(FilterState.posts) Text("Posts").tag(FilterState.posts)
Text("Posts & Replies").tag(FilterState.posts_and_replies) Text("Posts & Replies").tag(FilterState.posts_and_replies)
} }
@ -54,7 +113,7 @@ struct ContentView: View {
} }
func filter_event(_ ev: NostrEvent) -> Bool { func filter_event(_ ev: NostrEvent) -> Bool {
if viewModel.filter_state == .posts { if self.filter_state == .posts {
return !ev.is_reply(nil) return !ev.is_reply(nil)
} }
@ -63,41 +122,41 @@ struct ContentView: View {
func MainContent(damus: DamusState) -> some View { func MainContent(damus: DamusState) -> some View {
VStack { VStack {
NavigationLink(destination: MaybeProfileView, isActive: $viewModel.profile_open) { NavigationLink(destination: MaybeProfileView, isActive: $profile_open) {
EmptyView() EmptyView()
} }
NavigationLink(destination: MaybeThreadView, isActive: $viewModel.thread_open) { NavigationLink(destination: MaybeThreadView, isActive: $thread_open) {
EmptyView() EmptyView()
} }
NavigationLink(destination: MaybeSearchView, isActive: $viewModel.search_open) { NavigationLink(destination: MaybeSearchView, isActive: $search_open) {
EmptyView() EmptyView()
} }
switch viewModel.selected_timeline { switch selected_timeline {
case .search: case .search:
SearchHomeView(damus_state: viewModel.state!, model: SearchHomeModel(damus_state: viewModel.state!)) SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(damus_state: damus_state!))
case .home: case .home:
PostingTimelineView PostingTimelineView
case .notifications: case .notifications:
TimelineView(events: $viewModel.home.notifications, loading: $viewModel.home.loading, damus: damus, show_friend_icon: true, filter: { _ in true }) TimelineView(events: $home.notifications, loading: $home.loading, damus: damus, show_friend_icon: true, filter: { _ in true })
.navigationTitle("Notifications") .navigationTitle("Notifications")
case .dms: case .dms:
DirectMessagesView(damus_state: viewModel.state!) DirectMessagesView(damus_state: damus_state!)
.environmentObject(viewModel.home.dms) .environmentObject(home.dms)
case .none: case .none:
EmptyView() EmptyView()
} }
} }
.navigationBarTitle(viewModel.selected_timeline == .home ? "Home" : "Global", displayMode: .inline) .navigationBarTitle(selected_timeline == .home ? "Home" : "Global", displayMode: .inline)
} }
var MaybeSearchView: some View { var MaybeSearchView: some View {
Group { Group {
if let search = viewModel.active_search { if let search = self.active_search {
SearchView(appstate: viewModel.state!, search: SearchModel(pool: viewModel.state!.pool, search: search)) SearchView(appstate: damus_state!, search: SearchModel(pool: damus_state!.pool, search: search))
} else { } else {
EmptyView() EmptyView()
} }
@ -106,9 +165,9 @@ struct ContentView: View {
var MaybeThreadView: some View { var MaybeThreadView: some View {
Group { Group {
if let evid = viewModel.active_event_id { if let evid = self.active_event_id {
let thread_model = ThreadModel(evid: evid, damus_state: viewModel.state!) let thread_model = ThreadModel(evid: evid, damus_state: damus_state!)
ThreadView(thread: thread_model, damus: viewModel.state!, is_chatroom: false) ThreadView(thread: thread_model, damus: damus_state!, is_chatroom: false)
} else { } else {
EmptyView() EmptyView()
} }
@ -117,10 +176,10 @@ struct ContentView: View {
var MaybeProfileView: some View { var MaybeProfileView: some View {
Group { Group {
if let pk = viewModel.active_profile { if let pk = self.active_profile {
let profile_model = ProfileModel(pubkey: pk, damus: viewModel.state!) let profile_model = ProfileModel(pubkey: pk, damus: damus_state!)
let followers = FollowersModel(damus_state: viewModel.state!, target: pk) let followers = FollowersModel(damus_state: damus_state!, target: pk)
ProfileView(damus_state: viewModel.state!, profile: profile_model, followers: followers) ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers)
} else { } else {
EmptyView() EmptyView()
} }
@ -129,20 +188,20 @@ struct ContentView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
if let damus = viewModel.state { if let damus = self.damus_state {
NavigationView { NavigationView {
MainContent(damus: damus) MainContent(damus: damus)
.toolbar { .toolbar {
ToolbarItem(placement: .navigationBarLeading) { ToolbarItem(placement: .navigationBarLeading) {
let profile_model = ProfileModel(pubkey: viewModel.state!.pubkey, damus: viewModel.state!) let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!)
let followers_model = FollowersModel(damus_state: viewModel.state!, target: viewModel.state!.pubkey) let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey)
let prof_dest = ProfileView(damus_state: viewModel.state!, profile: profile_model, followers: followers_model) let prof_dest = ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers_model)
NavigationLink(destination: prof_dest) { NavigationLink(destination: prof_dest) {
/// Verify that the user has a profile picture, if not display a generic SF Symbol /// 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 /// (Resolves an in-app error where ``Robohash`` pictures are not generated so the button dissapears
if let picture = viewModel.state?.profiles.lookup(id: viewModel.pubkey)?.picture { if let picture = damus_state?.profiles.lookup(id: pubkey)?.picture {
ProfilePicView(pubkey: viewModel.state!.pubkey, size: 32, highlight: .none, profiles: viewModel.state!.profiles, picture: picture) ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, picture: picture)
} else { } else {
Image(systemName: "person.fill") Image(systemName: "person.fill")
} }
@ -152,14 +211,14 @@ struct ContentView: View {
ToolbarItem(placement: .navigationBarTrailing) { ToolbarItem(placement: .navigationBarTrailing) {
HStack(alignment: .center) { HStack(alignment: .center) {
if viewModel.home.signal.signal != viewModel.home.signal.max_signal { if home.signal.signal != home.signal.max_signal {
Text("\(viewModel.home.signal.signal)/\(viewModel.home.signal.max_signal)") Text("\(home.signal.signal)/\(home.signal.max_signal)")
.font(.callout) .font(.callout)
.foregroundColor(.gray) .foregroundColor(.gray)
} }
NavigationLink(destination: ConfigView(state: viewModel.state!)) { NavigationLink(destination: ConfigView(state: damus_state!)) {
Image(systemName: "gear") Label("", systemImage: "gear")
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
} }
@ -169,19 +228,19 @@ struct ContentView: View {
.navigationViewStyle(.stack) .navigationViewStyle(.stack)
} }
TabBar(new_events: $viewModel.home.new_events, selected: $viewModel.selected_timeline, action: viewModel.switch_timeline) TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline)
} }
.onAppear() { .onAppear() {
viewModel.connect() self.connect()
//KingfisherManager.shared.cache.clearDiskCache() //KingfisherManager.shared.cache.clearDiskCache()
setup_notifications() setup_notifications()
} }
.sheet(item: $viewModel.active_sheet) { item in .sheet(item: $active_sheet) { item in
switch item { switch item {
case .post: case .post:
PostView(replying_to: nil, references: []) PostView(replying_to: nil, references: [])
case .reply(let event): case .reply(let event):
ReplyView(replying_to: event, damus: viewModel.state!) ReplyView(replying_to: event, damus: damus_state!)
} }
} }
.onOpenURL { url in .onOpenURL { url in
@ -192,28 +251,28 @@ struct ContentView: View {
switch link { switch link {
case .ref(let ref): case .ref(let ref):
if ref.key == "p" { if ref.key == "p" {
viewModel.active_profile = ref.ref_id active_profile = ref.ref_id
viewModel.profile_open = true profile_open = true
} else if ref.key == "e" { } else if ref.key == "e" {
viewModel.active_event_id = ref.ref_id active_event_id = ref.ref_id
viewModel.thread_open = true thread_open = true
} }
case .filter(let filt): case .filter(let filt):
viewModel.active_search = filt active_search = filt
viewModel.search_open = true search_open = true
break break
// TODO: handle filter searches? // TODO: handle filter searches?
} }
} }
.onReceive(handle_notify(.boost)) { notif in .onReceive(handle_notify(.boost)) { notif in
guard let privkey = viewModel.privkey else { guard let privkey = self.privkey else {
return return
} }
let ev = notif.object as! NostrEvent let ev = notif.object as! NostrEvent
let boost = make_boost_event(pubkey: viewModel.pubkey, privkey: privkey, boosted: ev) let boost = make_boost_event(pubkey: pubkey, privkey: privkey, boosted: ev)
viewModel.state?.pool.send(.event(boost)) self.damus_state?.pool.send(.event(boost))
} }
.onReceive(handle_notify(.open_thread)) { obj in .onReceive(handle_notify(.open_thread)) { obj in
//let ev = obj.object as! NostrEvent //let ev = obj.object as! NostrEvent
@ -222,20 +281,20 @@ struct ContentView: View {
} }
.onReceive(handle_notify(.reply)) { notif in .onReceive(handle_notify(.reply)) { notif in
let ev = notif.object as! NostrEvent let ev = notif.object as! NostrEvent
viewModel.active_sheet = .reply(ev) self.active_sheet = .reply(ev)
} }
.onReceive(handle_notify(.like)) { like in .onReceive(handle_notify(.like)) { like in
} }
.onReceive(handle_notify(.broadcast_event)) { obj in .onReceive(handle_notify(.broadcast_event)) { obj in
let ev = obj.object as! NostrEvent let ev = obj.object as! NostrEvent
viewModel.state?.pool.send(.event(ev)) self.damus_state?.pool.send(.event(ev))
} }
.onReceive(handle_notify(.unfollow)) { notif in .onReceive(handle_notify(.unfollow)) { notif in
guard let privkey = viewModel.privkey else { guard let privkey = self.privkey else {
return return
} }
guard let damus = viewModel.state else { guard let damus = self.damus_state else {
return return
} }
@ -255,12 +314,12 @@ struct ContentView: View {
} }
} }
.onReceive(handle_notify(.follow)) { notif in .onReceive(handle_notify(.follow)) { notif in
guard let privkey = viewModel.privkey else { guard let privkey = self.privkey else {
return return
} }
let fnotify = notif.object as! FollowTarget let fnotify = notif.object as! FollowTarget
guard let damus = viewModel.state else { guard let damus = self.damus_state else {
return return
} }
@ -271,7 +330,7 @@ struct ContentView: View {
follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) { follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) {
notify(.followed, fnotify.pubkey) notify(.followed, fnotify.pubkey)
viewModel.state?.contacts.event = ev damus_state?.contacts.event = ev
switch fnotify { switch fnotify {
case .pubkey(let pk): case .pubkey(let pk):
@ -282,7 +341,7 @@ struct ContentView: View {
} }
} }
.onReceive(handle_notify(.post)) { obj in .onReceive(handle_notify(.post)) { obj in
guard let privkey = viewModel.privkey else { guard let privkey = self.privkey else {
return return
} }
@ -290,23 +349,70 @@ struct ContentView: View {
switch post_res { switch post_res {
case .post(let post): case .post(let post):
print("post \(post.content)") print("post \(post.content)")
let new_ev = post_to_event(post: post, privkey: privkey, pubkey: viewModel.pubkey) let new_ev = post_to_event(post: post, privkey: privkey, pubkey: pubkey)
viewModel.state?.pool.send(.event(new_ev)) self.damus_state?.pool.send(.event(new_ev))
case .cancel: case .cancel:
viewModel.active_sheet = nil active_sheet = nil
print("post cancelled") print("post cancelled")
} }
} }
.onReceive(timer) { n in .onReceive(timer) { n in
viewModel.state?.pool.connect_to_disconnected() self.damus_state?.pool.connect_to_disconnected()
}
}
func switch_timeline(_ timeline: Timeline) {
NotificationCenter.default.post(name: .switched_timeline, object: timeline)
if timeline == self.selected_timeline {
NotificationCenter.default.post(name: .scroll_to_top, object: nil)
return
}
self.selected_timeline = timeline
//NotificationCenter.default.post(name: .switched_timeline, object: timeline)
//self.selected_timeline = timeline
}
func add_relay(_ pool: RelayPool, _ relay: String) {
//add_rw_relay(pool, "wss://nostr-pub.wellorder.net")
add_rw_relay(pool, relay)
/*
let profile = Profile(name: relay, about: nil, picture: nil)
let ts = Int64(Date().timeIntervalSince1970)
let tsprofile = TimestampedProfile(profile: profile, timestamp: ts)
damus!.profiles.add(id: relay, profile: tsprofile)
*/
}
func connect() {
let pool = RelayPool()
for relay in BOOTSTRAP_RELAYS {
add_relay(pool, relay)
} }
pool.register_handler(sub_id: sub_id, handler: home.handle_event)
self.damus_state = DamusState(pool: pool, keypair: keypair,
likes: EventCounter(our_pubkey: pubkey),
boosts: EventCounter(our_pubkey: pubkey),
contacts: Contacts(),
tips: TipCounter(our_pubkey: pubkey),
profiles: Profiles(),
dms: home.dms
)
home.damus_state = self.damus_state!
pool.connect()
} }
} }
struct ContentView_Previews: PreviewProvider { struct ContentView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
ContentView() ContentView(keypair: Keypair(pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681", privkey: nil))
.environmentObject(DamusViewModel(with: Keypair(pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681", privkey: nil)))
} }
} }
@ -319,6 +425,31 @@ func get_since_time(last_event: NostrEvent?) -> Int64? {
return nil return nil
} }
func ws_nostr_event(relay: String, ev: WebSocketEvent) -> NostrEvent? {
switch ev {
case .binary(let dat):
return NostrEvent(content: "binary data? \(dat.count) bytes", pubkey: relay)
case .cancelled:
return NostrEvent(content: "cancelled", pubkey: relay)
case .connected:
return NostrEvent(content: "connected", pubkey: relay)
case .disconnected:
return NostrEvent(content: "disconnected", pubkey: relay)
case .error(let err):
return NostrEvent(content: "error \(err.debugDescription)", pubkey: relay)
case .text(let txt):
return NostrEvent(content: "text \(txt)", pubkey: relay)
case .pong:
return NostrEvent(content: "pong", pubkey: relay)
case .ping:
return NostrEvent(content: "ping", pubkey: relay)
case .viabilityChanged(let b):
return NostrEvent(content: "viabilityChanged \(b)", pubkey: relay)
case .reconnectSuggested(let b):
return NostrEvent(content: "reconnectSuggested \(b)", pubkey: relay)
}
}
func is_notification(ev: NostrEvent, pubkey: String) -> Bool { func is_notification(ev: NostrEvent, pubkey: String) -> Bool {
if ev.pubkey == pubkey { if ev.pubkey == pubkey {
return false return false
@ -338,6 +469,11 @@ extension UINavigationController: UIGestureRecognizerDelegate {
} }
} }
struct LastNotification {
let id: String
let created_at: Int64
}
func get_last_event(_ timeline: Timeline) -> LastNotification? { func get_last_event(_ timeline: Timeline) -> LastNotification? {
let str = timeline.rawValue let str = timeline.rawValue
let last = UserDefaults.standard.string(forKey: "last_\(str)") let last = UserDefaults.standard.string(forKey: "last_\(str)")
@ -413,3 +549,4 @@ func setup_notifications() {
} }
} }
} }

162
damus/DamusViewModel.swift

@ -1,162 +0,0 @@
//
// DamusViewModel.swift
// damus
//
// Created by Sam DuBois on 12/18/22.
//
import SwiftUI
import Starscream
import Kingfisher
/// Default relays to be used when setting up the user's account.
var BOOTSTRAP_RELAYS = [
"wss://relay.damus.io",
"wss://nostr-relay.wlvs.space",
"wss://nostr.oxtr.dev",
]
class DamusViewModel: ObservableObject {
// MARK: Constants and Variables
let sub_id = UUID().description
/// User Keypair object
var keypair: Keypair
var pubkey: String {
return keypair.pubkey
}
var privkey: String? {
return keypair.privkey
}
@Published var status: String = "Not connected"
@Published var state: DamusState? = nil
@Published var active_sheet: Sheets? = 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
@Published var home: HomeModel = HomeModel()
// MARK: Initializer
init(with key: Keypair) {
self.keypair = key
}
// MARK: Functionality
func switch_timeline(_ timeline: Timeline) {
NotificationCenter.default.post(name: .switched_timeline, object: timeline)
if timeline == self.selected_timeline {
NotificationCenter.default.post(name: .scroll_to_top, object: nil)
return
}
self.selected_timeline = timeline
//NotificationCenter.default.post(name: .switched_timeline, object: timeline)
//self.selected_timeline = timeline
}
func add_relay(_ pool: RelayPool, _ relay: String) {
//add_rw_relay(pool, "wss://nostr-pub.wellorder.net")
add_rw_relay(pool, relay)
/*
let profile = Profile(name: relay, about: nil, picture: nil)
let ts = Int64(Date().timeIntervalSince1970)
let tsprofile = TimestampedProfile(profile: profile, timestamp: ts)
damus!.profiles.add(id: relay, profile: tsprofile)
*/
}
func connect() {
let pool = RelayPool()
for relay in BOOTSTRAP_RELAYS {
add_relay(pool, relay)
}
pool.register_handler(sub_id: sub_id, handler: home.handle_event)
self.state = DamusState(pool: pool, keypair: keypair,
likes: EventCounter(our_pubkey: pubkey),
boosts: EventCounter(our_pubkey: pubkey),
contacts: Contacts(),
tips: TipCounter(our_pubkey: pubkey),
profiles: Profiles(),
dms: home.dms
)
home.damus_state = self.state!
pool.connect()
}
}
struct TimestampedProfile {
let profile: Profile
let timestamp: Int64
}
enum Sheets: Identifiable {
case post
case reply(NostrEvent)
var id: String {
switch self {
case .post: return "post"
case .reply(let ev): return "reply-" + ev.id
}
}
}
enum ThreadState {
case event_details
case chatroom
}
enum FilterState : Int {
case posts_and_replies = 1
case posts = 0
}
func ws_nostr_event(relay: String, ev: WebSocketEvent) -> NostrEvent? {
switch ev {
case .binary(let dat):
return NostrEvent(content: "binary data? \(dat.count) bytes", pubkey: relay)
case .cancelled:
return NostrEvent(content: "cancelled", pubkey: relay)
case .connected:
return NostrEvent(content: "connected", pubkey: relay)
case .disconnected:
return NostrEvent(content: "disconnected", pubkey: relay)
case .error(let err):
return NostrEvent(content: "error \(err.debugDescription)", pubkey: relay)
case .text(let txt):
return NostrEvent(content: "text \(txt)", pubkey: relay)
case .pong:
return NostrEvent(content: "pong", pubkey: relay)
case .ping:
return NostrEvent(content: "ping", pubkey: relay)
case .viabilityChanged(let b):
return NostrEvent(content: "viabilityChanged \(b)", pubkey: relay)
case .reconnectSuggested(let b):
return NostrEvent(content: "reconnectSuggested \(b)", pubkey: relay)
}
}
struct LastNotification {
let id: String
let created_at: Int64
}

2
damus/Views/TimelineView.swift

@ -62,8 +62,6 @@ struct InnerTimelineRedactedView: View {
struct TimelineView: View { struct TimelineView: View {
@EnvironmentObject var viewModel: DamusViewModel
@Binding var events: [NostrEvent] @Binding var events: [NostrEvent]
@Binding var loading: Bool @Binding var loading: Bool

3
damus/damusApp.swift

@ -26,8 +26,7 @@ struct MainView: View {
var body: some View { var body: some View {
Group { Group {
if let kp = keypair, !needs_setup { if let kp = keypair, !needs_setup {
ContentView() ContentView(keypair: kp)
.environmentObject(DamusViewModel(with: kp))
} else { } else {
SetupView() SetupView()
.onReceive(handle_notify(.login)) { notif in .onReceive(handle_notify(.login)) { notif in

Loading…
Cancel
Save