... to a persistent setting, and try to load it from NostrDB on app start. This commit causes the user's contact list event ID to be saved persistently as a user-specific setting, and to be loaded immediately after startup from the local NostrDB instance. This helps improve reliability around contact lists, since we previously relied on fetching that contact list from other relays. Eventually we will not need the event ID to be stored at all, as we will be able to query NostrDB, but for now having the latest event ID persistently stored will allow us to get around this limitation in the cleanest possible way (i.e. without having to store the event itself into another mechanism, and migrating it later to NostrDB) Other notes: - It uses a mechanism similar to other user settings, so it is pubkey-specific and should handle login/logout cases Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Reviewed-by: William Casarin <jb55@jb55.com> Link: 20240422230912.65056-2-daniel@daquino.me Signed-off-by: William Casarin <jb55@jb55.com>
101 lines
2.9 KiB
Swift
101 lines
2.9 KiB
Swift
//
|
|
// Contacts.swift
|
|
// damus
|
|
//
|
|
// Created by William Casarin on 2022-05-14.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
class Contacts {
|
|
private var friends: Set<Pubkey> = Set()
|
|
private var friend_of_friends: Set<Pubkey> = Set()
|
|
/// Tracks which friends are friends of a given pubkey.
|
|
private var pubkey_to_our_friends = [Pubkey : Set<Pubkey>]()
|
|
|
|
let our_pubkey: Pubkey
|
|
var delegate: ContactsDelegate? = nil
|
|
var event: NostrEvent? {
|
|
didSet {
|
|
guard let event else { return }
|
|
self.delegate?.latest_contact_event_changed(new_event: event)
|
|
}
|
|
}
|
|
|
|
init(our_pubkey: Pubkey) {
|
|
self.our_pubkey = our_pubkey
|
|
}
|
|
|
|
func remove_friend(_ pubkey: Pubkey) {
|
|
friends.remove(pubkey)
|
|
|
|
pubkey_to_our_friends.forEach {
|
|
pubkey_to_our_friends[$0.key]?.remove(pubkey)
|
|
}
|
|
}
|
|
|
|
func get_friend_list() -> Set<Pubkey> {
|
|
return friends
|
|
}
|
|
|
|
func get_followed_hashtags() -> Set<String> {
|
|
guard let ev = self.event else { return Set() }
|
|
return Set(ev.referenced_hashtags.map({ $0.hashtag }))
|
|
}
|
|
|
|
func follows(hashtag: Hashtag) -> Bool {
|
|
guard let ev = self.event else { return false }
|
|
return ev.referenced_hashtags.first(where: { $0 == hashtag }) != nil
|
|
}
|
|
|
|
func add_friend_pubkey(_ pubkey: Pubkey) {
|
|
friends.insert(pubkey)
|
|
}
|
|
|
|
func add_friend_contact(_ contact: NostrEvent) {
|
|
friends.insert(contact.pubkey)
|
|
for pk in contact.referenced_pubkeys {
|
|
friend_of_friends.insert(pk)
|
|
|
|
// Exclude themself and us.
|
|
if contact.pubkey != our_pubkey && contact.pubkey != pk {
|
|
if pubkey_to_our_friends[pk] == nil {
|
|
pubkey_to_our_friends[pk] = Set<Pubkey>()
|
|
}
|
|
|
|
pubkey_to_our_friends[pk]?.insert(contact.pubkey)
|
|
}
|
|
}
|
|
}
|
|
|
|
func is_friend_of_friend(_ pubkey: Pubkey) -> Bool {
|
|
return friend_of_friends.contains(pubkey)
|
|
}
|
|
|
|
func is_in_friendosphere(_ pubkey: Pubkey) -> Bool {
|
|
return friends.contains(pubkey) || friend_of_friends.contains(pubkey)
|
|
}
|
|
|
|
func is_friend(_ pubkey: Pubkey) -> Bool {
|
|
return friends.contains(pubkey)
|
|
}
|
|
|
|
func is_friend_or_self(_ pubkey: Pubkey) -> Bool {
|
|
return pubkey == our_pubkey || is_friend(pubkey)
|
|
}
|
|
|
|
func follow_state(_ pubkey: Pubkey) -> FollowState {
|
|
return is_friend(pubkey) ? .follows : .unfollows
|
|
}
|
|
|
|
/// Gets the list of pubkeys of our friends who follow the given pubkey.
|
|
func get_friended_followers(_ pubkey: Pubkey) -> [Pubkey] {
|
|
return Array((pubkey_to_our_friends[pubkey] ?? Set()))
|
|
}
|
|
}
|
|
|
|
/// Delegate protocol for `Contacts`. Use this to listen to significant updates from a `Contacts` instance
|
|
protocol ContactsDelegate {
|
|
func latest_contact_event_changed(new_event: NostrEvent)
|
|
}
|