ndb/txn: make transactions failable
Since there may be situations where we close and re-open the database,
we need to make sure transactions fail when the database is not open.
Make NdbTxn an init?() constructor and check for ndb.closed. If it's
closed, then fail transaction construction.
This fixes crashes during high database activity when switching from
background to foreground and vice-versa.
Fixes: da2bdad18d ("nostrdb: close database when backgrounded")
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ?? "")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -24,10 +24,10 @@ func follow_btn_txt(_ fs: FollowState, follows_you: Bool) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
func followedByString<Y>(txn: NdbTxn<Y>, _ 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 {
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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: " ")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -34,7 +34,7 @@ struct ProfileZapLinkView<Content: View>: 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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user