diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 36664959..2d77d57e 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -396,11 +396,21 @@ struct ContentView: View { } .onReceive(handle_notify(.unfollow)) { notif in guard let state = self.damus_state else { return } - handle_unfollow_notif(state: state, notif: notif) + guard let unfollow = handle_unfollow_notif(state: state, notif: notif) else { return } + } + .onReceive(handle_notify(.unfollowed)) { notif in + guard let state = self.damus_state else { return } + let unfollow = notif.object as! ReferencedId + home.resubscribe(.unfollowing(unfollow)) } .onReceive(handle_notify(.follow)) { notif in guard let state = self.damus_state else { return } - handle_follow_notif(state: state, notif: notif) + guard handle_follow_notif(state: state, notif: notif) else { return } + } + .onReceive(handle_notify(.followed)) { notif in + guard let state = self.damus_state else { return } + let follow = notif.object as! ReferencedId + home.resubscribe(.following) } .onReceive(handle_notify(.post)) { notif in guard let state = self.damus_state, @@ -875,16 +885,17 @@ func timeline_name(_ timeline: Timeline?) -> String { } } -func handle_unfollow(state: DamusState, unfollow: ReferencedId) { +@discardableResult +func handle_unfollow(state: DamusState, unfollow: ReferencedId) -> Bool { guard let keypair = state.keypair.to_full() else { - return + return false } let old_contacts = state.contacts.event guard let ev = unfollow_reference(postbox: state.postbox, our_contacts: old_contacts, keypair: keypair, unfollow: unfollow) else { - return + return false } notify(.unfollowed, unfollow) @@ -895,13 +906,20 @@ func handle_unfollow(state: DamusState, unfollow: ReferencedId) { state.contacts.remove_friend(unfollow.ref_id) state.user_search_cache.updateOwnContactsPetnames(id: state.pubkey, oldEvent: old_contacts, newEvent: ev) } + + return true } -func handle_unfollow_notif(state: DamusState, notif: Notification) { +func handle_unfollow_notif(state: DamusState, notif: Notification) -> ReferencedId? { let target = notif.object as! FollowTarget let pk = target.pubkey - handle_unfollow(state: state, unfollow: .p(pk)) + let ref = ReferencedId.p(pk) + if handle_unfollow(state: state, unfollow: ref) { + return ref + } + + return nil } @discardableResult diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift index 47e49fe7..b3247ccb 100644 --- a/damus/Models/HomeModel.swift +++ b/damus/Models/HomeModel.swift @@ -23,6 +23,40 @@ struct NewEventsBits: OptionSet { static let notifications: NewEventsBits = [.zaps, .likes, .reposts, .mentions] } +enum Resubscribe { + case following + case unfollowing(ReferencedId) +} + +enum HomeResubFilter { + case pubkey(String) + case hashtag(String) + + init?(from: ReferencedId) { + if from.key == "p" { + self = .pubkey(from.ref_id) + return + } else if from.key == "t" { + self = .hashtag(from.ref_id) + return + } + + return nil + } + + func filter(contacts: Contacts, ev: NostrEvent) -> Bool { + switch self { + case .pubkey(let pk): + return ev.pubkey == pk + case .hashtag(let ht): + if contacts.is_friend(ev.pubkey) { + return false + } + return ev.references(id: ht, key: "t") + } + } +} + class HomeModel { // Don't trigger a user notification for events older than a certain age static let event_max_age_for_notification: TimeInterval = 12 * 60 * 60 @@ -36,6 +70,7 @@ class HomeModel { var done_init: Bool = false var incoming_dms: [NostrEvent] = [] let dm_debouncer = Debouncer(interval: 0.5) + let resub_debouncer = Debouncer(interval: 3.0) var should_debounce_dms = true let home_subid = UUID().description @@ -90,6 +125,31 @@ class HomeModel { } } + func resubscribe(_ resubbing: Resubscribe) { + if self.should_debounce_dms { + // don't resub on initial load + return + } + + print("hit resub debouncer") + + resub_debouncer.debounce { + print("resub") + self.unsubscribe_to_home_filters() + + switch resubbing { + case .following: + break + case .unfollowing(let r): + if let filter = HomeResubFilter(from: r) { + self.events.filter { ev in !filter.filter(contacts: self.damus_state.contacts, ev: ev) } + } + } + + self.subscribe_to_home_filters() + } + } + func process_event(sub_id: String, relay_id: String, ev: NostrEvent) { if has_sub_id_event(sub_id: sub_id, ev_id: ev.id) { return @@ -639,32 +699,34 @@ func add_contact_if_friend(contacts: Contacts, ev: NostrEvent) { func load_our_contacts(state: DamusState, m_old_ev: NostrEvent?, ev: NostrEvent) { let contacts = state.contacts - var new_pks = Set() + var new_refs = Set() // our contacts for tag in ev.tags { - if tag.count >= 2 && tag[0] == "p" { - new_pks.insert(tag[1]) - } + guard let ref = tag_to_refid(tag) else { continue } + new_refs.insert(ref) } - var old_pks = Set() + var old_refs = Set() // find removed contacts if let old_ev = m_old_ev { for tag in old_ev.tags { - if tag.count >= 2 && tag[0] == "p" { - old_pks.insert(tag[1]) - } + guard let ref = tag_to_refid(tag) else { continue } + old_refs.insert(ref) } } - let diff = new_pks.symmetricDifference(old_pks) - for pk in diff { - if new_pks.contains(pk) { - notify(.followed, pk) - contacts.add_friend_pubkey(pk) + let diff = new_refs.symmetricDifference(old_refs) + for ref in diff { + if new_refs.contains(ref) { + notify(.followed, ref) + if ref.key == "p" { + contacts.add_friend_pubkey(ref.ref_id) + } } else { - notify(.unfollowed, pk) - contacts.remove_friend(pk) + notify(.unfollowed, ref) + if ref.key == "p" { + contacts.remove_friend(ref.ref_id) + } } }