diff --git a/DamusNotificationService/NotificationFormatter.swift b/DamusNotificationService/NotificationFormatter.swift index 8c9bec54..615eaacc 100644 --- a/DamusNotificationService/NotificationFormatter.swift +++ b/DamusNotificationService/NotificationFormatter.swift @@ -118,9 +118,9 @@ struct NotificationFormatter { let src = zap.request.ev let pk = zap.is_anon ? ANON_PUBKEY : src.pubkey - let name = profiles.lookup(id: pk).map { profile in - Profile.displayName(profile: profile, pubkey: pk).displayName.truncate(maxLength: 50) - }.value + let profile_txn = profiles.lookup(id: pk) + let profile = profile_txn?.unsafeUnownedValue + let name = Profile.displayName(profile: profile, pubkey: pk).displayName.truncate(maxLength: 50) let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0)) let formattedSats = format_msats_abbrev(zap.invoice.amount) diff --git a/DamusNotificationService/NotificationService.swift b/DamusNotificationService/NotificationService.swift index 352fd1b6..4d3e8c46 100644 --- a/DamusNotificationService/NotificationService.swift +++ b/DamusNotificationService/NotificationService.swift @@ -28,7 +28,7 @@ class NotificationService: UNNotificationServiceExtension { Log.debug("Got nostr event push notification from pubkey %s", for: .push_notifications, nostr_event.pubkey.hex()) guard let state = NotificationExtensionState(), - let display_name = state.ndb.lookup_profile(nostr_event.pubkey).unsafeUnownedValue?.profile?.display_name // We are not holding the txn here. + let display_name = state.ndb.lookup_profile(nostr_event.pubkey)?.unsafeUnownedValue?.profile?.display_name // We are not holding the txn here. else { // Something failed to initialize so let's go for the next best thing guard let improved_content = NotificationFormatter.shared.format_message(event: nostr_event) else { diff --git a/damus/Components/NIP05Badge.swift b/damus/Components/NIP05Badge.swift index db7956af..9d8d9280 100644 --- a/damus/Components/NIP05Badge.swift +++ b/damus/Components/NIP05Badge.swift @@ -45,7 +45,7 @@ struct NIP05Badge: View { } var username_matches_nip05: Bool { - guard let name = profiles.lookup(id: pubkey).map({ p in p?.name }).value + guard let name = profiles.lookup(id: pubkey)?.map({ p in p?.name }).value else { return false } diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 98a5f1bf..e7e0a61c 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -370,14 +370,9 @@ struct ContentView: View { // wallet with an associated guard let ds = self.damus_state, let lud16 = nwc.lud16, - let keypair = ds.keypair.to_full() - else { - return - } - - let profile_txn = ds.profiles.lookup(id: ds.pubkey) - - guard let profile = profile_txn.unsafeUnownedValue, + let keypair = ds.keypair.to_full(), + let profile_txn = ds.profiles.lookup(id: ds.pubkey), + let profile = profile_txn.unsafeUnownedValue, lud16 != profile.lud16 else { return } @@ -514,10 +509,9 @@ struct ContentView: View { .onReceive(handle_notify(.onlyzaps_mode)) { hide in home.filter_events() - guard let ds = damus_state else { return } - let profile_txn = ds.profiles.lookup(id: ds.pubkey) - - guard let profile = profile_txn.unsafeUnownedValue, + guard let ds = damus_state, + let profile_txn = ds.profiles.lookup(id: ds.pubkey), + let profile = profile_txn.unsafeUnownedValue, let keypair = ds.keypair.to_full() else { return @@ -534,9 +528,9 @@ struct ContentView: View { } }, message: { if let pubkey = self.muting { - let name = damus_state!.profiles.lookup(id: pubkey).map { profile in - Profile.displayName(profile: profile, pubkey: pubkey).username.truncate(maxLength: 50) - }.value + let profile_txn = damus_state!.profiles.lookup(id: pubkey) + let profile = profile_txn?.unsafeUnownedValue + let name = Profile.displayName(profile: profile, pubkey: pubkey).username.truncate(maxLength: 50) Text("\(name) has been muted", comment: "Alert message that informs a user was muted.") } else { Text("User has been muted", comment: "Alert message that informs a user was muted.") @@ -595,9 +589,9 @@ struct ContentView: View { } }, message: { if let pubkey = muting { - let name = damus_state?.profiles.lookup(id: pubkey).map({ profile in - Profile.displayName(profile: profile, pubkey: pubkey).username.truncate(maxLength: 50) - }).value ?? "unknown" + let profile_txn = damus_state?.profiles.lookup(id: pubkey) + let profile = profile_txn?.unsafeUnownedValue + let name = Profile.displayName(profile: profile, pubkey: pubkey).username.truncate(maxLength: 50) Text("Mute \(name)?", comment: "Alert message prompt to ask if a user should be muted.") } else { Text("Could not find user to mute...", comment: "Alert message to indicate that the muted user could not be found.") @@ -865,7 +859,8 @@ func find_event_with_subid(state: DamusState, query query_: FindEvent, subid: St switch query { case .profile(let pubkey): - if let record = state.ndb.lookup_profile(pubkey).unsafeUnownedValue, + if let profile_txn = state.ndb.lookup_profile(pubkey), + let record = profile_txn.unsafeUnownedValue, record.profile != nil { callback(.profile(pubkey)) diff --git a/damus/Models/EventsModel.swift b/damus/Models/EventsModel.swift index 4923fd17..7ce906db 100644 --- a/damus/Models/EventsModel.swift +++ b/damus/Models/EventsModel.swift @@ -66,7 +66,9 @@ class EventsModel: ObservableObject { case .auth: break case .eose: - let txn = NdbTxn(ndb: self.state.ndb) + guard let txn = NdbTxn(ndb: self.state.ndb) else { + return + } load_profiles(context: "events_model", profiles_subid: profiles_id, relay_id: relay_id, load: .from_events(events), damus_state: state, txn: txn) } } diff --git a/damus/Models/FollowersModel.swift b/damus/Models/FollowersModel.swift index ab78c4d2..b74a08b8 100644 --- a/damus/Models/FollowersModel.swift +++ b/damus/Models/FollowersModel.swift @@ -83,7 +83,7 @@ class FollowersModel: ObservableObject { case .eose(let sub_id): if sub_id == self.sub_id { - let txn = NdbTxn(ndb: self.damus_state.ndb) + guard let txn = NdbTxn(ndb: self.damus_state.ndb) else { return } load_profiles(relay_id: relay_id, txn: txn) } else if sub_id == self.profiles_id { damus_state.pool.unsubscribe(sub_id: profiles_id, to: [relay_id]) diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift index 2e794fe5..4210ce3e 100644 --- a/damus/Models/HomeModel.swift +++ b/damus/Models/HomeModel.swift @@ -414,8 +414,10 @@ class HomeModel { print(msg) case .eose(let sub_id): - - let txn = NdbTxn(ndb: damus_state.ndb) + guard let txn = NdbTxn(ndb: damus_state.ndb) else { + return + } + if sub_id == dms_subid { var dms = dms.dms.flatMap { $0.events } dms.append(contentsOf: incoming_dms) diff --git a/damus/Models/NoteContent.swift b/damus/Models/NoteContent.swift index 889a4ba6..4ccaa681 100644 --- a/damus/Models/NoteContent.swift +++ b/damus/Models/NoteContent.swift @@ -187,7 +187,7 @@ func mention_str(_ m: Mention, profiles: Profiles) -> CompatibleText case .pubkey(let pk): let npub = bech32_pubkey(pk) let profile_txn = profiles.lookup(id: pk) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue let disp = Profile.displayName(profile: profile, pubkey: pk).username.truncate(maxLength: 50) var attributedString = AttributedString(stringLiteral: "@\(disp)") attributedString.link = URL(string: "damus:nostr:\(npub)") diff --git a/damus/Models/NotificationsManager.swift b/damus/Models/NotificationsManager.swift index 3618cd3e..66a5d3a0 100644 --- a/damus/Models/NotificationsManager.swift +++ b/damus/Models/NotificationsManager.swift @@ -21,6 +21,7 @@ func process_local_notification(state: HeadlessDamusState, event ev: NostrEvent) guard let local_notification = generate_local_notification_object(from: ev, state: state) else { return } + create_local_notification(profiles: state.profiles, notify: local_notification) } @@ -76,7 +77,8 @@ func generate_local_notification_object(from ev: NostrEvent, state: HeadlessDamu } else if type == .like, state.settings.like_notification, let evid = ev.referenced_ids.last, - let liked_event = state.ndb.lookup_note(evid).unsafeUnownedValue // We are only accessing it temporarily to generate notification content + let txn = state.ndb.lookup_note(evid), + let liked_event = txn.unsafeUnownedValue // We are only accessing it temporarily to generate notification content { let content_preview = render_notification_content_preview(ev: liked_event, profiles: state.profiles, keypair: state.keypair) return LocalNotification(type: .like, event: ev, target: liked_event, content: content_preview) @@ -135,9 +137,9 @@ func render_notification_content_preview(ev: NostrEvent, profiles: Profiles, key } func event_author_name(profiles: Profiles, pubkey: Pubkey) -> String { - return profiles.lookup(id: pubkey).map({ profile in - Profile.displayName(profile: profile, pubkey: pubkey).username.truncate(maxLength: 50) - }).value + let profile_txn = profiles.lookup(id: pubkey) + let profile = profile_txn?.unsafeUnownedValue + return Profile.displayName(profile: profile, pubkey: pubkey).username.truncate(maxLength: 50) } @MainActor @@ -173,8 +175,8 @@ func process_zap_event(state: HeadlessDamusState, ev: NostrEvent, completion: @e return } - guard let lnurl = state.profiles.lookup_with_timestamp(ptag) - .map({ pr in pr?.lnurl }).value else { + guard let txn = state.profiles.lookup_with_timestamp(ptag), + let lnurl = txn.map({ pr in pr?.lnurl }).value else { completion(.failed) return } @@ -221,7 +223,8 @@ func get_zap_target_pubkey(ev: NostrEvent, ndb: Ndb) -> Pubkey? { } // we can't trust the p tag on note zaps because they can be faked - guard let pk = ndb.lookup_note(etag).unsafeUnownedValue?.pubkey else { + guard let txn = ndb.lookup_note(etag), + let pk = txn.unsafeUnownedValue?.pubkey else { // We don't have the event in cache so we can't check the pubkey. // We could return this as an invalid zap but that wouldn't be correct diff --git a/damus/Models/ProfileModel.swift b/damus/Models/ProfileModel.swift index 76e294a9..d0392659 100644 --- a/damus/Models/ProfileModel.swift +++ b/damus/Models/ProfileModel.swift @@ -128,7 +128,7 @@ class ProfileModel: ObservableObject, Equatable { break //notify(.notice, notice) case .eose: - let txn = NdbTxn(ndb: damus.ndb) + guard let txn = NdbTxn(ndb: damus.ndb) else { return } if resp.subid == sub_id { load_profiles(context: "profile", profiles_subid: prof_subid, relay_id: relay_id, load: .from_events(events.events), damus_state: damus, txn: txn) } diff --git a/damus/Models/SearchHomeModel.swift b/damus/Models/SearchHomeModel.swift index 07344f66..84ae5911 100644 --- a/damus/Models/SearchHomeModel.swift +++ b/damus/Models/SearchHomeModel.swift @@ -83,7 +83,7 @@ class SearchHomeModel: ObservableObject { // global events are not realtime unsubscribe(to: relay_id) - let txn = NdbTxn(ndb: damus_state.ndb) + guard let txn = NdbTxn(ndb: damus_state.ndb) else { return } load_profiles(context: "universe", profiles_subid: profiles_subid, relay_id: relay_id, load: .from_events(events.all_events), damus_state: damus_state, txn: txn) } diff --git a/damus/Models/SearchModel.swift b/damus/Models/SearchModel.swift index 9dc49723..b61231af 100644 --- a/damus/Models/SearchModel.swift +++ b/damus/Models/SearchModel.swift @@ -80,7 +80,7 @@ class SearchModel: ObservableObject { self.loading = false if sub_id == self.sub_id { - let txn = NdbTxn(ndb: state.ndb) + guard let txn = NdbTxn(ndb: state.ndb) else { return } load_profiles(context: "search", profiles_subid: self.profiles_subid, relay_id: relay_id, load: .from_events(self.events.all_events), damus_state: state, txn: txn) } } diff --git a/damus/Models/ThreadModel.swift b/damus/Models/ThreadModel.swift index 06f63c7b..dbc86929 100644 --- a/damus/Models/ThreadModel.swift +++ b/damus/Models/ThreadModel.swift @@ -120,7 +120,7 @@ class ThreadModel: ObservableObject { } if sub_id == self.base_subid { - let txn = NdbTxn(ndb: damus_state.ndb) + guard let txn = NdbTxn(ndb: damus_state.ndb) else { return } 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) } } diff --git a/damus/Models/ZapsModel.swift b/damus/Models/ZapsModel.swift index b20f3957..49870f2a 100644 --- a/damus/Models/ZapsModel.swift +++ b/damus/Models/ZapsModel.swift @@ -55,7 +55,7 @@ class ZapsModel: ObservableObject { break case .eose: let events = state.events.lookup_zaps(target: target).map { $0.request.ev } - let txn = NdbTxn(ndb: state.ndb) + guard let txn = NdbTxn(ndb: state.ndb) else { return } load_profiles(context: "zaps_model", profiles_subid: profiles_subid, relay_id: relay_id, load: .from_events(events), damus_state: state, txn: txn) case .event(_, let ev): guard ev.kind == 9735, diff --git a/damus/Nostr/Profiles.swift b/damus/Nostr/Profiles.swift index ad9a5dde..71c5327b 100644 --- a/damus/Nostr/Profiles.swift +++ b/damus/Nostr/Profiles.swift @@ -73,24 +73,27 @@ class Profiles { profile_data(pubkey).zapper } - func lookup_with_timestamp(_ pubkey: Pubkey) -> NdbTxn { - return ndb.lookup_profile(pubkey) + func lookup_with_timestamp(_ pubkey: Pubkey) -> NdbTxn? { + ndb.lookup_profile(pubkey) } - func lookup_by_key(key: ProfileKey) -> NdbTxn { - return ndb.lookup_profile_by_key(key: key) + func lookup_by_key(key: ProfileKey) -> NdbTxn? { + ndb.lookup_profile_by_key(key: key) } func search(_ query: String, limit: Int, txn: NdbTxn) -> [Pubkey] { - return ndb.search_profile(query, limit: limit, txn: txn) + ndb.search_profile(query, limit: limit, txn: txn) } - func lookup(id: Pubkey) -> NdbTxn { - return ndb.lookup_profile(id).map({ pr in pr?.profile }) + func lookup(id: Pubkey) -> NdbTxn? { + guard let txn = ndb.lookup_profile(id) else { + return nil + } + return txn.map({ pr in pr?.profile }) } func lookup_key_by_pubkey(_ pubkey: Pubkey) -> ProfileKey? { - return ndb.lookup_profile_key(pubkey) + ndb.lookup_profile_key(pubkey) } func has_fresh_profile(id: Pubkey, txn: NdbTxn) -> Bool { diff --git a/damus/Views/ActionBar/EventActionBar.swift b/damus/Views/ActionBar/EventActionBar.swift index e0aa1ca1..3775f99d 100644 --- a/damus/Views/ActionBar/EventActionBar.swift +++ b/damus/Views/ActionBar/EventActionBar.swift @@ -28,7 +28,9 @@ struct EventActionBar: View { } var lnurl: String? { - damus_state.profiles.lookup_with_timestamp(event.pubkey).map({ pr in pr?.lnurl }).value + damus_state.profiles.lookup_with_timestamp(event.pubkey)?.map({ pr in + pr?.lnurl + }).value } var show_like: Bool { diff --git a/damus/Views/BannerImageView.swift b/damus/Views/BannerImageView.swift index dee4e11e..9cdd2b18 100644 --- a/damus/Views/BannerImageView.swift +++ b/damus/Views/BannerImageView.swift @@ -78,11 +78,12 @@ struct BannerImageView: View { var body: some View { InnerBannerImageView(disable_animation: disable_animation, url: get_banner_url(banner: banner, pubkey: pubkey, profiles: profiles)) .onReceive(handle_notify(.profile_updated)) { updated in - guard updated.pubkey == self.pubkey else { + guard updated.pubkey == self.pubkey, + let profile_txn = profiles.lookup(id: updated.pubkey) + else { return } - let profile_txn = profiles.lookup(id: updated.pubkey) let profile = profile_txn.unsafeUnownedValue if let bannerImage = profile?.banner, bannerImage != self.banner { self.banner = bannerImage @@ -92,7 +93,7 @@ struct BannerImageView: View { } func get_banner_url(banner: String?, pubkey: Pubkey, profiles: Profiles) -> URL? { - let bannerUrlString = banner ?? profiles.lookup(id: pubkey).map({ p in p?.banner }).value ?? "" + let bannerUrlString = banner ?? profiles.lookup(id: pubkey)?.map({ p in p?.banner }).value ?? "" if let url = URL(string: bannerUrlString) { return url } diff --git a/damus/Views/Events/Components/ReplyDescription.swift b/damus/Views/Events/Components/ReplyDescription.swift index 90a64f2a..1a1c529c 100644 --- a/damus/Views/Events/Components/ReplyDescription.swift +++ b/damus/Views/Events/Components/ReplyDescription.swift @@ -38,7 +38,9 @@ func reply_desc(ndb: Ndb, event: NostrEvent, replying_to: NostrEvent?, locale: L return NSLocalizedString("Replying to self", bundle: bundle, comment: "Label to indicate that the user is replying to themself.") } - let profile_txn = NdbTxn(ndb: ndb) + guard let profile_txn = NdbTxn(ndb: ndb) else { + return "" + } let names: [String] = pubkeys.map { pk in let prof = ndb.lookup_profile_with_txn(pk, txn: profile_txn) diff --git a/damus/Views/FollowingView.swift b/damus/Views/FollowingView.swift index 16dab49c..434ff7ff 100644 --- a/damus/Views/FollowingView.swift +++ b/damus/Views/FollowingView.swift @@ -151,7 +151,7 @@ struct FollowingView: View { } .tabViewStyle(.page(indexDisplayMode: .never)) .onAppear { - let txn = NdbTxn(ndb: self.damus_state.ndb) + guard let txn = NdbTxn(ndb: self.damus_state.ndb) else { return } following.subscribe(txn: txn) } .onDisappear { diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift index 47fceb7f..fc69b9ca 100644 --- a/damus/Views/NoteContentView.swift +++ b/damus/Views/NoteContentView.swift @@ -303,7 +303,7 @@ struct NoteContentView: View { class NoteArtifactsParts { var parts: [ArtifactPart] var words: Int - + init(parts: [ArtifactPart], words: Int) { self.parts = parts self.words = words diff --git a/damus/Views/Notifications/NotificationsView.swift b/damus/Views/Notifications/NotificationsView.swift index b68d78b8..3ff89e28 100644 --- a/damus/Views/Notifications/NotificationsView.swift +++ b/damus/Views/Notifications/NotificationsView.swift @@ -61,7 +61,7 @@ struct NotificationsView: View { var mystery: some View { let profile_txn = state.profiles.lookup(id: state.pubkey) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue return VStack(spacing: 20) { Text("Wake up, \(Profile.displayName(profile: profile, pubkey: state.pubkey).displayName.truncate(maxLength: 50))", comment: "Text telling the user to wake up, where the argument is their display name.") Text("You are dreaming...", comment: "Text telling the user that they are dreaming.") diff --git a/damus/Views/Onboarding/SuggestedUsersViewModel.swift b/damus/Views/Onboarding/SuggestedUsersViewModel.swift index f78ae7b6..a711e32f 100644 --- a/damus/Views/Onboarding/SuggestedUsersViewModel.swift +++ b/damus/Views/Onboarding/SuggestedUsersViewModel.swift @@ -36,7 +36,7 @@ class SuggestedUsersViewModel: ObservableObject { func suggestedUser(pubkey: Pubkey) -> SuggestedUser? { let profile_txn = damus_state.profiles.lookup(id: pubkey) - if let profile = profile_txn.unsafeUnownedValue, + if let profile = profile_txn?.unsafeUnownedValue, let user = SuggestedUser(name: profile.name, about: profile.about, picture: profile.picture, pubkey: pubkey) { return user } diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift index 5803e3db..8fabf0fe 100644 --- a/damus/Views/PostView.swift +++ b/damus/Views/PostView.swift @@ -181,7 +181,7 @@ struct PostView: View { } let profile_txn = damus_state.profiles.lookup(id: pubkey) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue return user_tag_attr_string(profile: profile, pubkey: pubkey) } diff --git a/damus/Views/Posting/UserSearch.swift b/damus/Views/Posting/UserSearch.swift index e783eff2..c4136e35 100644 --- a/damus/Views/Posting/UserSearch.swift +++ b/damus/Views/Posting/UserSearch.swift @@ -17,7 +17,7 @@ struct UserSearch: View { @EnvironmentObject var tagModel: TagModel var users: [Pubkey] { - let txn = NdbTxn(ndb: damus_state.ndb) + guard let txn = NdbTxn(ndb: damus_state.ndb) else { return [] } return search_profiles(profiles: damus_state.profiles, search: search, txn: txn).sorted { a, b in let aFriendTypePriority = get_friend_type(contacts: damus_state.contacts, pubkey: a)?.priority ?? 0 let bFriendTypePriority = get_friend_type(contacts: damus_state.contacts, pubkey: b)?.priority ?? 0 @@ -33,7 +33,7 @@ struct UserSearch: View { func on_user_tapped(pk: Pubkey) { let profile_txn = damus_state.profiles.lookup(id: pk) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue let user_tag = user_tag_attr_string(profile: profile, pubkey: pk) appendUserTag(withTag: user_tag) diff --git a/damus/Views/Profile/EditMetadataView.swift b/damus/Views/Profile/EditMetadataView.swift index 07e3e5ce..99eb473d 100644 --- a/damus/Views/Profile/EditMetadataView.swift +++ b/damus/Views/Profile/EditMetadataView.swift @@ -30,7 +30,8 @@ struct EditMetadataView: View { init(damus_state: DamusState) { self.damus_state = damus_state - let data = damus_state.profiles.lookup(id: damus_state.pubkey).unsafeUnownedValue + let profile_txn = damus_state.profiles.lookup(id: damus_state.pubkey) + let data = profile_txn?.unsafeUnownedValue _name = State(initialValue: data?.name ?? "") _display_name = State(initialValue: data?.display_name ?? "") diff --git a/damus/Views/Profile/EventProfileName.swift b/damus/Views/Profile/EventProfileName.swift index 19435e22..5a31822b 100644 --- a/damus/Views/Profile/EventProfileName.swift +++ b/damus/Views/Profile/EventProfileName.swift @@ -24,7 +24,7 @@ struct EventProfileName: View { self.damus_state = damus self.pubkey = pubkey self.size = size - let donation = damus.ndb.lookup_profile(pubkey).map({ p in p?.profile?.damus_donation }).value + let donation = damus.ndb.lookup_profile(pubkey)?.map({ p in p?.profile?.damus_donation }).value self._donation = State(wrappedValue: donation) is_purple_user = nil } @@ -65,7 +65,7 @@ struct EventProfileName: View { var body: some View { let profile_txn = damus_state.profiles.lookup(id: pubkey) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue HStack(spacing: 2) { switch current_display_name(profile) { case .one(let one): @@ -109,7 +109,7 @@ struct EventProfileName: View { } let profile_txn = damus_state.profiles.lookup(id: update.pubkey) - guard let profile = profile_txn.unsafeUnownedValue else { return } + guard let profile = profile_txn?.unsafeUnownedValue else { return } let display_name = Profile.displayName(profile: profile, pubkey: pubkey) if display_name != self.display_name { diff --git a/damus/Views/Profile/ProfileName.swift b/damus/Views/Profile/ProfileName.swift index b7911b01..92b8cdbe 100644 --- a/damus/Views/Profile/ProfileName.swift +++ b/damus/Views/Profile/ProfileName.swift @@ -87,7 +87,7 @@ struct ProfileName: View { var body: some View { let profile_txn = damus_state.profiles.lookup(id: pubkey) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue HStack(spacing: 2) { Text(verbatim: "\(prefix)\(name_choice(profile: profile))") diff --git a/damus/Views/Profile/ProfileNameView.swift b/damus/Views/Profile/ProfileNameView.swift index 44c585b4..f3feed2b 100644 --- a/damus/Views/Profile/ProfileNameView.swift +++ b/damus/Views/Profile/ProfileNameView.swift @@ -17,7 +17,7 @@ struct ProfileNameView: View { Group { VStack(alignment: .leading) { let profile_txn = self.damus.profiles.lookup(id: pubkey) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue switch Profile.displayName(profile: profile, pubkey: pubkey) { case .one: diff --git a/damus/Views/Profile/ProfilePicView.swift b/damus/Views/Profile/ProfilePicView.swift index cc3773ed..20efd043 100644 --- a/damus/Views/Profile/ProfilePicView.swift +++ b/damus/Views/Profile/ProfilePicView.swift @@ -84,7 +84,7 @@ struct ProfilePicView: View { } func get_lnurl() -> String? { - return profiles.lookup_with_timestamp(pubkey).unsafeUnownedValue?.lnurl + return profiles.lookup_with_timestamp(pubkey)?.unsafeUnownedValue?.lnurl } var body: some View { @@ -102,7 +102,7 @@ struct ProfilePicView: View { } case .remote(pubkey: let pk): let profile_txn = profiles.lookup(id: pk) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue if let pic = profile?.picture { self.picture = pic } @@ -126,7 +126,7 @@ struct ProfilePicView: View { } func get_profile_url(picture: String?, pubkey: Pubkey, profiles: Profiles) -> URL { - let pic = picture ?? profiles.lookup(id: pubkey).map({ $0?.picture }).value ?? robohash(pubkey) + let pic = picture ?? profiles.lookup(id: pubkey)?.map({ $0?.picture }).value ?? robohash(pubkey) if let url = URL(string: pic) { return url } diff --git a/damus/Views/Profile/ProfilePictureSelector.swift b/damus/Views/Profile/ProfilePictureSelector.swift index 77fc2d0f..5559746c 100644 --- a/damus/Views/Profile/ProfilePictureSelector.swift +++ b/damus/Views/Profile/ProfilePictureSelector.swift @@ -43,7 +43,7 @@ struct EditProfilePictureView: View { if let profile_url { return profile_url } else if let state = damus_state, - let picture = state.profiles.lookup(id: pubkey).map({ pr in pr?.picture }).value { + let picture = state.profiles.lookup(id: pubkey)?.map({ pr in pr?.picture }).value { return URL(string: picture) } else { return profile_url ?? URL(string: robohash(pubkey)) diff --git a/damus/Views/Profile/ProfileView.swift b/damus/Views/Profile/ProfileView.swift index 0b9e1a44..47dc0fe8 100644 --- a/damus/Views/Profile/ProfileView.swift +++ b/damus/Views/Profile/ProfileView.swift @@ -24,10 +24,10 @@ func follow_btn_txt(_ fs: FollowState, follows_you: Bool) -> String { } } -func followedByString(txn: NdbTxn, _ friend_intersection: [Pubkey], ndb: Ndb, locale: Locale = Locale.current) -> String { +func followedByString(_ friend_intersection: [Pubkey], ndb: Ndb, locale: Locale = Locale.current) -> String { let bundle = bundleForLocale(locale: locale) let names: [String] = friend_intersection.prefix(3).map { pk in - let profile = ndb.lookup_profile_with_txn(pk, txn: txn)?.profile + let profile = ndb.lookup_profile(pk)?.unsafeUnownedValue?.profile return Profile.displayName(profile: profile, pubkey: pk).username.truncate(maxLength: 20) } @@ -331,7 +331,7 @@ struct ProfileView: View { var aboutSection: some View { VStack(alignment: .leading, spacing: 8.0) { let profile_txn = damus_state.profiles.lookup_with_timestamp(profile.pubkey) - let profile_data = profile_txn.unsafeUnownedValue + let profile_data = profile_txn?.unsafeUnownedValue nameSection(profile_data: profile_data) @@ -398,7 +398,7 @@ struct ProfileView: View { NavigationLink(value: Route.FollowersYouKnow(friendedFollowers: friended_followers, followers: followers)) { HStack { CondensedProfilePicturesView(state: damus_state, pubkeys: friended_followers, maxPictures: 3) - let followedByString = followedByString(txn: profile_txn, friended_followers, ndb: damus_state.ndb) + let followedByString = followedByString(friended_followers, ndb: damus_state.ndb) Text(followedByString) .font(.subheadline).foregroundColor(.gray) .multilineTextAlignment(.leading) @@ -499,7 +499,7 @@ extension View { func check_nip05_validity(pubkey: Pubkey, profiles: Profiles) { let profile_txn = profiles.lookup(id: pubkey) - guard let profile = profile_txn.unsafeUnownedValue, + guard let profile = profile_txn?.unsafeUnownedValue, let nip05 = profile.nip05, profiles.is_validated(pubkey) == nil else { diff --git a/damus/Views/ProfileActionSheetView.swift b/damus/Views/ProfileActionSheetView.swift index fedbd486..bcec3929 100644 --- a/damus/Views/ProfileActionSheetView.swift +++ b/damus/Views/ProfileActionSheetView.swift @@ -30,7 +30,7 @@ struct ProfileActionSheetView: View { func profile_data() -> ProfileRecord? { let profile_txn = damus_state.profiles.lookup_with_timestamp(profile.pubkey) - return profile_txn.unsafeUnownedValue + return profile_txn?.unsafeUnownedValue } func get_profile() -> Profile? { diff --git a/damus/Views/QRCodeView.swift b/damus/Views/QRCodeView.swift index af91d2d6..772b3756 100644 --- a/damus/Views/QRCodeView.swift +++ b/damus/Views/QRCodeView.swift @@ -119,10 +119,11 @@ struct QRCodeView: View { var QRView: some View { VStack(alignment: .center) { let profile_txn = damus_state.profiles.lookup(id: pubkey) - let profile = profile_txn.unsafeUnownedValue - let our_profile = damus_state.ndb.lookup_profile_with_txn(damus_state.pubkey, txn: profile_txn) + let profile = profile_txn?.unsafeUnownedValue + let our_profile_txn = damus_state.profiles.lookup(id: damus_state.pubkey) + let our_profile = our_profile_txn?.unsafeUnownedValue - if our_profile?.profile?.picture != nil { + if our_profile?.picture != nil { ProfilePicView(pubkey: pubkey, size: 90.0, highlight: .custom(DamusColors.white, 3.0), profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation) .padding(.top, 50) } else { diff --git a/damus/Views/ReplyView.swift b/damus/Views/ReplyView.swift index 27042166..7686017b 100644 --- a/damus/Views/ReplyView.swift +++ b/damus/Views/ReplyView.swift @@ -24,11 +24,10 @@ struct ReplyView: View { var ReplyingToSection: some View { HStack { Group { - let txn = NdbTxn(ndb: damus.ndb) let names = references .map { pubkey in let pk = pubkey - let prof = damus.ndb.lookup_profile_with_txn(pk, txn: txn)?.profile + let prof = damus.ndb.lookup_profile(pk)?.unsafeUnownedValue?.profile return "@" + Profile.displayName(profile: prof, pubkey: pk).username.truncate(maxLength: 50) } .joined(separator: " ") diff --git a/damus/Views/Search/PullDownSearch.swift b/damus/Views/Search/PullDownSearch.swift index e23ba1dd..21e98559 100644 --- a/damus/Views/Search/PullDownSearch.swift +++ b/damus/Views/Search/PullDownSearch.swift @@ -31,7 +31,7 @@ struct PullDownSearchView: View { } do { - let txn = NdbTxn(ndb: state.ndb) + guard let txn = NdbTxn(ndb: state.ndb) else { return } for note_key in note_keys { guard let note = state.ndb.lookup_note_by_key_with_txn(note_key, txn: txn) else { continue diff --git a/damus/Views/SearchResultsView.swift b/damus/Views/SearchResultsView.swift index b2748880..18f6e221 100644 --- a/damus/Views/SearchResultsView.swift +++ b/damus/Views/SearchResultsView.swift @@ -108,11 +108,11 @@ struct SearchResultsView: View { } .frame(maxHeight: .infinity) .onAppear { - let txn = NdbTxn.init(ndb: damus_state.ndb) + guard let txn = NdbTxn.init(ndb: damus_state.ndb) else { return } self.result = search_for_string(profiles: damus_state.profiles, search: search, txn: txn) } .onChange(of: search) { new in - let txn = NdbTxn.init(ndb: damus_state.ndb) + guard let txn = NdbTxn.init(ndb: damus_state.ndb) else { return } self.result = search_for_string(profiles: damus_state.profiles, search: search, txn: txn) } } diff --git a/damus/Views/SideMenuView.swift b/damus/Views/SideMenuView.swift index 4b2bbfc0..ca5968f4 100644 --- a/damus/Views/SideMenuView.swift +++ b/damus/Views/SideMenuView.swift @@ -90,7 +90,7 @@ struct SideMenuView: View { var TopProfile: some View { let profile_txn = damus_state.profiles.lookup(id: damus_state.pubkey) - let profile = profile_txn.unsafeUnownedValue + let profile = profile_txn?.unsafeUnownedValue return VStack(alignment: .leading, spacing: verticalSpacing) { HStack { ProfilePicView(pubkey: damus_state.pubkey, size: 60, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation) diff --git a/damus/Views/Wallet/WalletView.swift b/damus/Views/Wallet/WalletView.swift index 4eaf6449..ff93a745 100644 --- a/damus/Views/Wallet/WalletView.swift +++ b/damus/Views/Wallet/WalletView.swift @@ -165,7 +165,7 @@ struct WalletView: View { } .onChange(of: settings.donation_percent) { p in let profile_txn = damus_state.profiles.lookup(id: damus_state.pubkey) - guard let profile = profile_txn.unsafeUnownedValue else { + guard let profile = profile_txn?.unsafeUnownedValue else { return } @@ -177,7 +177,7 @@ struct WalletView: View { let profile_txn = damus_state.profiles.lookup(id: damus_state.pubkey) guard let keypair = damus_state.keypair.to_full(), - let profile = profile_txn.unsafeUnownedValue, + let profile = profile_txn?.unsafeUnownedValue, model.initial_percent != profile.damus_donation else { return diff --git a/damus/Views/Zaps/ProfileZapLinkView.swift b/damus/Views/Zaps/ProfileZapLinkView.swift index 89e480ee..ad130327 100644 --- a/damus/Views/Zaps/ProfileZapLinkView.swift +++ b/damus/Views/Zaps/ProfileZapLinkView.swift @@ -34,7 +34,7 @@ struct ProfileZapLinkView: View { self.action = action let profile_txn = damus_state.profiles.lookup_with_timestamp(pubkey) - let record = profile_txn.unsafeUnownedValue + let record = profile_txn?.unsafeUnownedValue self.reactions_enabled = record?.profile?.reactions ?? true self.lud16 = record?.profile?.lud06 self.lnurl = record?.lnurl diff --git a/damus/Views/Zaps/ZapTypePicker.swift b/damus/Views/Zaps/ZapTypePicker.swift index a9416d4d..a7e3b3db 100644 --- a/damus/Views/Zaps/ZapTypePicker.swift +++ b/damus/Views/Zaps/ZapTypePicker.swift @@ -98,7 +98,7 @@ func zap_type_desc(type: ZapType, profiles: Profiles, pubkey: Pubkey) -> String return NSLocalizedString("No one will see that you zapped", comment: "Description of anonymous zap type where the zap is sent anonymously and does not identify the user who sent it.") case .priv: let prof_txn = profiles.lookup(id: pubkey) - let prof = prof_txn.unsafeUnownedValue + let prof = prof_txn?.unsafeUnownedValue let name = Profile.displayName(profile: prof, pubkey: pubkey).username.truncate(maxLength: 50) return String.localizedStringWithFormat(NSLocalizedString("private_zap_description", value: "Only '%@' will see that you zapped them", comment: "Description of private zap type where the zap is sent privately and does not identify the user to the public."), name) case .non_zap: diff --git a/nostrdb/Ndb.swift b/nostrdb/Ndb.swift index 3fdea3d2..b9956460 100644 --- a/nostrdb/Ndb.swift +++ b/nostrdb/Ndb.swift @@ -186,6 +186,7 @@ class Ndb { throw DatabaseError.failed_open } + self.closed = false self.ndb = db } @@ -198,7 +199,7 @@ class Ndb { } func text_search(query: String, limit: Int = 32, order: NdbSearchOrder = .newest_first) -> [NoteKey] { - let txn = NdbTxn(ndb: self) + guard let txn = NdbTxn(ndb: self) else { return [] } var results = ndb_text_search_results() let res = query.withCString { q in let order = order == .newest_first ? NDB_ORDER_DESCENDING : NDB_ORDER_ASCENDING @@ -243,7 +244,7 @@ class Ndb { return note_ids } - func lookup_note_by_key(_ key: NoteKey) -> NdbTxn { + func lookup_note_by_key(_ key: NoteKey) -> NdbTxn? { return NdbTxn(ndb: self) { txn in lookup_note_by_key_with_txn(key, txn: txn) } @@ -301,7 +302,7 @@ class Ndb { lookup_profile_by_key_inner(key, txn: txn) } - func lookup_profile_by_key(key: ProfileKey) -> NdbTxn { + func lookup_profile_by_key(key: ProfileKey) -> NdbTxn? { return NdbTxn(ndb: self) { txn in lookup_profile_by_key_inner(key, txn: txn) } @@ -312,9 +313,13 @@ class Ndb { } func lookup_profile_key(_ pubkey: Pubkey) -> ProfileKey? { - return NdbTxn(ndb: self) { txn in + guard let txn = NdbTxn(ndb: self, with: { txn in lookup_profile_key_with_txn(pubkey, txn: txn) - }.value + }) else { + return nil + } + + return txn.value } func lookup_profile_key_with_txn(_ pubkey: Pubkey, txn: NdbTxn) -> ProfileKey? { @@ -342,17 +347,23 @@ class Ndb { } func lookup_note_key(_ id: NoteId) -> NoteKey? { - NdbTxn(ndb: self, with: { txn in lookup_note_key_with_txn(id, txn: txn) }).value + guard let txn = NdbTxn(ndb: self, with: { txn in + lookup_note_key_with_txn(id, txn: txn) + }) else { + return nil + } + + return txn.value } - func lookup_note(_ id: NoteId) -> NdbTxn { - return NdbTxn(ndb: self) { txn in + func lookup_note(_ id: NoteId) -> NdbTxn? { + NdbTxn(ndb: self) { txn in lookup_note_with_txn_inner(id: id, txn: txn) } } - func lookup_profile(_ pubkey: Pubkey) -> NdbTxn { - return NdbTxn(ndb: self) { txn in + func lookup_profile(_ pubkey: Pubkey) -> NdbTxn? { + NdbTxn(ndb: self) { txn in lookup_profile_with_txn_inner(pubkey: pubkey, txn: txn) } } diff --git a/nostrdb/NdbTxn.swift b/nostrdb/NdbTxn.swift index a7628902..76e9ccaf 100644 --- a/nostrdb/NdbTxn.swift +++ b/nostrdb/NdbTxn.swift @@ -18,8 +18,9 @@ class NdbTxn { var moved: Bool var inherited: Bool - init(ndb: Ndb, with: (NdbTxn) -> T = { _ in () }) { - #if TXNDEBUG + init?(ndb: Ndb, with: (NdbTxn) -> T = { _ in () }) { + guard !ndb.closed else { return nil } +#if TXNDEBUG txn_count += 1 print("opening transaction \(txn_count)") #endif @@ -31,11 +32,7 @@ class NdbTxn { self.txn = ndb_txn() let ok = ndb_begin_query(ndb.ndb.ndb, &self.txn) != 0 if !ok { - self.moved = false - self.txn = ndb_txn() - self.inherited = true - self.val = with(self) - return + return nil } Thread.current.threadDictionary["ndb_txn"] = self.txn self.inherited = false