ThreadModel.swift (4723B)
1 // 2 // ThreadModel.swift 3 // damus 4 // 5 // Created by William Casarin on 2022-04-19. 6 // 7 8 import Foundation 9 10 /// manages the lifetime of a thread 11 class ThreadModel: ObservableObject { 12 @Published var event: NostrEvent 13 let original_event: NostrEvent 14 var event_map: Set<NostrEvent> 15 16 init(event: NostrEvent, damus_state: DamusState) { 17 self.damus_state = damus_state 18 self.event_map = Set() 19 self.event = event 20 self.original_event = event 21 add_event(event, keypair: damus_state.keypair) 22 } 23 24 var is_original: Bool { 25 return original_event.id == event.id 26 } 27 28 let damus_state: DamusState 29 30 let profiles_subid = UUID().description 31 let base_subid = UUID().description 32 let meta_subid = UUID().description 33 34 var subids: [String] { 35 return [profiles_subid, base_subid, meta_subid] 36 } 37 38 func unsubscribe() { 39 self.damus_state.pool.remove_handler(sub_id: base_subid) 40 self.damus_state.pool.remove_handler(sub_id: meta_subid) 41 self.damus_state.pool.remove_handler(sub_id: profiles_subid) 42 self.damus_state.pool.unsubscribe(sub_id: base_subid) 43 self.damus_state.pool.unsubscribe(sub_id: meta_subid) 44 self.damus_state.pool.unsubscribe(sub_id: profiles_subid) 45 print("unsubscribing from thread \(event.id) with sub_id \(base_subid)") 46 } 47 48 @discardableResult 49 func set_active_event(_ ev: NostrEvent, keypair: Keypair) -> Bool { 50 self.event = ev 51 add_event(ev, keypair: keypair) 52 53 //self.objectWillChange.send() 54 return false 55 } 56 57 func subscribe() { 58 var meta_events = NostrFilter() 59 var quote_events = NostrFilter() 60 var event_filter = NostrFilter() 61 var ref_events = NostrFilter() 62 63 let thread_id = event.thread_id(keypair: .empty) 64 65 ref_events.referenced_ids = [thread_id, event.id] 66 ref_events.kinds = [.text] 67 ref_events.limit = 1000 68 69 event_filter.ids = [thread_id, event.id] 70 71 meta_events.referenced_ids = [event.id] 72 73 var kinds: [NostrKind] = [.zap, .text, .boost] 74 if !damus_state.settings.onlyzaps_mode { 75 kinds.append(.like) 76 } 77 meta_events.kinds = kinds 78 meta_events.limit = 1000 79 80 quote_events.kinds = [.text] 81 quote_events.quotes = [event.id] 82 quote_events.limit = 1000 83 84 let base_filters = [event_filter, ref_events] 85 let meta_filters = [meta_events, quote_events] 86 87 print("subscribing to thread \(event.id) with sub_id \(base_subid)") 88 damus_state.pool.subscribe(sub_id: base_subid, filters: base_filters, handler: handle_event) 89 damus_state.pool.subscribe(sub_id: meta_subid, filters: meta_filters, handler: handle_event) 90 } 91 92 func add_event(_ ev: NostrEvent, keypair: Keypair) { 93 if event_map.contains(ev) { 94 return 95 } 96 97 damus_state.events.upsert(ev) 98 damus_state.replies.count_replies(ev, keypair: keypair) 99 damus_state.events.add_replies(ev: ev, keypair: keypair) 100 101 event_map.insert(ev) 102 objectWillChange.send() 103 } 104 105 @MainActor 106 func handle_event(relay_id: RelayURL, ev: NostrConnectionEvent) { 107 108 let (sub_id, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: ev) { sid, ev in 109 guard subids.contains(sid) else { 110 return 111 } 112 113 if ev.known_kind == .zap { 114 process_zap_event(state: damus_state, ev: ev) { zap in 115 116 } 117 } else if ev.is_textlike { 118 // handle thread quote reposts, we just count them instead of 119 // adding them to the thread 120 if let target = ev.is_quote_repost, target == self.event.id { 121 //let _ = self.damus_state.quote_reposts.add_event(ev, target: target) 122 } else { 123 self.add_event(ev, keypair: damus_state.keypair) 124 } 125 } 126 } 127 128 guard done, let sub_id, subids.contains(sub_id) else { 129 return 130 } 131 132 if sub_id == self.base_subid { 133 guard let txn = NdbTxn(ndb: damus_state.ndb) else { return } 134 load_profiles(context: "thread", profiles_subid: self.profiles_subid, relay_id: relay_id, load: .from_events(Array(event_map)), damus_state: damus_state, txn: txn) 135 } 136 } 137 138 } 139 140 141 func get_top_zap(events: EventCache, evid: NoteId) -> Zapping? { 142 return events.get_cache_data(evid).zaps_model.zaps.first(where: { zap in 143 !zap.request.marked_hidden 144 }) 145 }