nostrdb: add profiles to nostrdb
This adds profiles to nostrdb - Remove in-memory Profiles caches, nostrdb is as fast as an in-memory cache - Remove ProfileDatabase and just use nostrdb directly Changelog-Changed: Use nostrdb for profiles
This commit is contained in:
@@ -34,6 +34,7 @@ struct DamusState {
|
||||
let user_search_cache: UserSearchCache
|
||||
let music: MusicController?
|
||||
let video: VideoController
|
||||
let ndb: Ndb
|
||||
|
||||
@discardableResult
|
||||
func add_zap(zap: Zapping) -> Bool {
|
||||
@@ -67,12 +68,12 @@ struct DamusState {
|
||||
let kp = Keypair(pubkey: empty_pub, privkey: nil)
|
||||
|
||||
return DamusState.init(
|
||||
pool: RelayPool(),
|
||||
pool: RelayPool(ndb: .empty),
|
||||
keypair: Keypair(pubkey: empty_pub, privkey: empty_sec),
|
||||
likes: EventCounter(our_pubkey: empty_pub),
|
||||
boosts: EventCounter(our_pubkey: empty_pub),
|
||||
contacts: Contacts(our_pubkey: empty_pub),
|
||||
profiles: Profiles(user_search_cache: user_search_cache),
|
||||
profiles: Profiles(user_search_cache: user_search_cache, ndb: .empty),
|
||||
dms: DirectMessagesModel(our_pubkey: empty_pub),
|
||||
previews: PreviewCache(),
|
||||
zaps: Zaps(our_pubkey: empty_pub),
|
||||
@@ -81,9 +82,9 @@ struct DamusState {
|
||||
relay_filters: RelayFilters(our_pubkey: empty_pub),
|
||||
relay_model_cache: RelayModelCache(),
|
||||
drafts: Drafts(),
|
||||
events: EventCache(),
|
||||
events: EventCache(ndb: .empty),
|
||||
bookmarks: BookmarksManager(pubkey: empty_pub),
|
||||
postbox: PostBox(pool: RelayPool()),
|
||||
postbox: PostBox(pool: RelayPool(ndb: .empty)),
|
||||
bootstrap_relays: [],
|
||||
replies: ReplyCounter(our_pubkey: empty_pub),
|
||||
muted_threads: MutedThreadsManager(keypair: kp),
|
||||
@@ -91,7 +92,8 @@ struct DamusState {
|
||||
nav: NavigationCoordinator(),
|
||||
user_search_cache: user_search_cache,
|
||||
music: nil,
|
||||
video: VideoController()
|
||||
video: VideoController(),
|
||||
ndb: .empty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,10 +77,7 @@ class FollowersModel: ObservableObject {
|
||||
|
||||
if ev.known_kind == .contacts {
|
||||
handle_contact_event(ev)
|
||||
} else if ev.known_kind == .metadata {
|
||||
process_metadata_event(events: damus_state.events, our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
case .notice(let msg):
|
||||
print("followingmodel notice: \(msg)")
|
||||
|
||||
|
||||
@@ -54,22 +54,6 @@ class FollowingModel {
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
|
||||
switch ev {
|
||||
case .ws_event:
|
||||
break
|
||||
case .nostr_event(let nev):
|
||||
switch nev {
|
||||
case .ok:
|
||||
break
|
||||
case .event(_, let ev):
|
||||
if ev.kind == 0 {
|
||||
process_metadata_event(events: damus_state.events, our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("followingmodel notice: \(msg)")
|
||||
case .eose:
|
||||
break
|
||||
}
|
||||
}
|
||||
// don't need to do anything here really
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ class HomeModel {
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
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
|
||||
@@ -169,7 +170,8 @@ class HomeModel {
|
||||
case .contacts:
|
||||
handle_contact_event(sub_id: sub_id, relay_id: relay_id, ev: ev)
|
||||
case .metadata:
|
||||
handle_metadata_event(ev)
|
||||
// profile metadata processing is handled by nostrdb
|
||||
break
|
||||
case .list:
|
||||
handle_list_event(ev)
|
||||
case .boost:
|
||||
@@ -195,6 +197,7 @@ class HomeModel {
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func handle_status_event(_ ev: NostrEvent) {
|
||||
guard let st = UserStatus(ev: ev) else {
|
||||
return
|
||||
@@ -248,7 +251,8 @@ class HomeModel {
|
||||
nwc_success(state: self.damus_state, resp: resp)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@MainActor
|
||||
func handle_zap_event(_ ev: NostrEvent) {
|
||||
process_zap_event(damus_state: damus_state, ev: ev) { zapres in
|
||||
guard case .done(let zap) = zapres,
|
||||
@@ -373,7 +377,7 @@ class HomeModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@MainActor
|
||||
func handle_event(relay_id: String, conn_event: NostrConnectionEvent) {
|
||||
switch conn_event {
|
||||
case .ws_event(let ev):
|
||||
@@ -582,10 +586,6 @@ class HomeModel {
|
||||
damus_state.contacts.set_mutelist(ev)
|
||||
}
|
||||
|
||||
func handle_metadata_event(_ ev: NostrEvent) {
|
||||
process_metadata_event(events: damus_state.events, our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
func get_last_event_of_kind(relay_id: String, kind: UInt32) -> NostrEvent? {
|
||||
guard let m = last_event_of_kind[relay_id] else {
|
||||
last_event_of_kind[relay_id] = [:]
|
||||
@@ -791,45 +791,6 @@ func print_filters(relay_id: String?, filters groups: [[NostrFilter]]) {
|
||||
}
|
||||
*/
|
||||
|
||||
func process_metadata_profile(our_pubkey: Pubkey, profiles: Profiles, profile: Profile, ev: NostrEvent) {
|
||||
var old_nip05: String? = nil
|
||||
let mprof = profiles.lookup_with_timestamp(id: ev.pubkey)
|
||||
|
||||
if let mprof {
|
||||
old_nip05 = mprof.profile.nip05
|
||||
if mprof.event.created_at > ev.created_at {
|
||||
// skip if we already have an newer profile
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if old_nip05 != profile.nip05 {
|
||||
// if it's been validated before, invalidate it now
|
||||
profiles.invalidate_nip05(ev.pubkey)
|
||||
}
|
||||
|
||||
let tprof = TimestampedProfile(profile: profile, timestamp: ev.created_at, event: ev)
|
||||
profiles.add(id: ev.pubkey, profile: tprof)
|
||||
|
||||
// load pfps asap
|
||||
|
||||
var changed = false
|
||||
|
||||
let picture = tprof.profile.picture ?? robohash(ev.pubkey)
|
||||
if URL(string: picture) != nil {
|
||||
changed = true
|
||||
}
|
||||
|
||||
let banner = tprof.profile.banner ?? ""
|
||||
if URL(string: banner) != nil {
|
||||
changed = true
|
||||
}
|
||||
|
||||
if changed {
|
||||
notify(.profile_updated(pubkey: ev.pubkey, profile: profile))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this, let nostrdb handle all validation
|
||||
func guard_valid_event(events: EventCache, ev: NostrEvent, callback: @escaping () -> Void) {
|
||||
let validated = events.is_event_valid(ev.id)
|
||||
@@ -856,24 +817,6 @@ func guard_valid_event(events: EventCache, ev: NostrEvent, callback: @escaping (
|
||||
}
|
||||
}
|
||||
|
||||
func process_metadata_event(events: EventCache, our_pubkey: Pubkey, profiles: Profiles, ev: NostrEvent, completion: ((Profile?) -> Void)? = nil) {
|
||||
guard_valid_event(events: events, ev: ev) {
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
guard let profile: Profile = decode_data(Data(ev.content.utf8)) else {
|
||||
completion?(nil)
|
||||
return
|
||||
}
|
||||
|
||||
profile.cache_lnurl()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
process_metadata_profile(our_pubkey: our_pubkey, profiles: profiles, profile: profile, ev: ev)
|
||||
completion?(profile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func robohash(_ pk: Pubkey) -> String {
|
||||
return "https://robohash.org/" + pk.hex()
|
||||
}
|
||||
@@ -1394,6 +1337,7 @@ func get_zap_target_pubkey(ev: NostrEvent, events: EventCache) -> Pubkey? {
|
||||
return pk
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func process_zap_event(damus_state: DamusState, ev: NostrEvent, completion: @escaping (ProcessZapResult) -> Void) {
|
||||
// These are zap notifications
|
||||
guard let ptag = get_zap_target_pubkey(ev: ev, events: damus_state.events) else {
|
||||
@@ -1417,17 +1361,13 @@ func process_zap_event(damus_state: DamusState, ev: NostrEvent, completion: @esc
|
||||
return
|
||||
}
|
||||
|
||||
guard let profile = damus_state.profiles.lookup(id: ptag) else {
|
||||
completion(.failed)
|
||||
return
|
||||
}
|
||||
|
||||
guard let lnurl = profile.lnurl else {
|
||||
guard let record = damus_state.profiles.lookup_with_timestamp(ptag),
|
||||
let lnurl = record.lnurl else {
|
||||
completion(.failed)
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
Task { [lnurl] in
|
||||
guard let zapper = await fetch_zapper_from_lnurl(lnurls: damus_state.lnurls, pubkey: ptag, lnurl: lnurl) else {
|
||||
completion(.failed)
|
||||
return
|
||||
|
||||
@@ -102,8 +102,6 @@ class ProfileModel: ObservableObject, Equatable {
|
||||
}
|
||||
} else if ev.known_kind == .contacts {
|
||||
handle_profile_contact_event(ev)
|
||||
} else if ev.known_kind == .metadata {
|
||||
process_metadata_event(events: damus.events, our_pubkey: damus.pubkey, profiles: damus.profiles, ev: ev)
|
||||
}
|
||||
seen_event.insert(ev.id)
|
||||
}
|
||||
|
||||
@@ -139,21 +139,13 @@ func load_profiles(profiles_subid: String, relay_id: String, load: PubkeysToLoad
|
||||
authors: authors)
|
||||
|
||||
damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in
|
||||
let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in
|
||||
guard sub_id == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
if ev.known_kind == .metadata {
|
||||
process_metadata_event(events: damus_state.events, our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guard done && sid == profiles_subid else {
|
||||
guard case .nostr_event(let ev) = conn_ev,
|
||||
case .eose = ev,
|
||||
sub_id == profiles_subid
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
print("done loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id])
|
||||
}
|
||||
|
||||
@@ -97,7 +97,8 @@ class ThreadModel: ObservableObject {
|
||||
event_map.insert(ev)
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
||||
|
||||
@MainActor
|
||||
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
|
||||
|
||||
let (sub_id, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: ev) { sid, ev in
|
||||
@@ -105,9 +106,7 @@ class ThreadModel: ObservableObject {
|
||||
return
|
||||
}
|
||||
|
||||
if ev.known_kind == .metadata {
|
||||
process_metadata_event(events: damus_state.events, our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||
} else if ev.known_kind == .zap {
|
||||
if ev.known_kind == .zap {
|
||||
process_zap_event(damus_state: damus_state, ev: ev) { zap in
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import Foundation
|
||||
/// Cache of searchable users by name, display_name, NIP-05 identifier, or own contact list petname.
|
||||
/// Optimized for fast searches of substrings by using a Trie.
|
||||
/// Optimal for performing user searches that could be initiated by typing quickly on a keyboard into a text input field.
|
||||
|
||||
// TODO: replace with lmdb (the b tree should handle this just fine ?)
|
||||
// we just need a name to profile index
|
||||
class UserSearchCache {
|
||||
private let trie = Trie<Pubkey>()
|
||||
|
||||
@@ -19,6 +22,7 @@ class UserSearchCache {
|
||||
}
|
||||
|
||||
/// Computes the differences between an old profile, if it exists, and a new profile, and updates the user search cache accordingly.
|
||||
@MainActor
|
||||
func updateProfile(id: Pubkey, profiles: Profiles, oldProfile: Profile?, newProfile: Profile) {
|
||||
// Remove searchable keys tied to the old profile if they differ from the new profile
|
||||
// to keep the trie clean without empty nodes while avoiding excessive graph searching.
|
||||
@@ -38,6 +42,7 @@ class UserSearchCache {
|
||||
}
|
||||
|
||||
/// Adds a profile to the user search cache.
|
||||
@MainActor
|
||||
private func addProfile(id: Pubkey, profiles: Profiles, profile: Profile) {
|
||||
// Searchable by name.
|
||||
if let name = profile.name {
|
||||
|
||||
@@ -37,7 +37,8 @@ class ZapsModel: ObservableObject {
|
||||
func unsubscribe() {
|
||||
state.pool.unsubscribe(sub_id: zaps_subid)
|
||||
}
|
||||
|
||||
|
||||
@MainActor
|
||||
func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) {
|
||||
guard case .nostr_event(let resp) = conn_ev else {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user