Fix broken DM rendering
Currently NostrDB does not seem to handle encryption/decryption of DMs. Since NostrDB now controls the block parsing process and fetches note contents directly from the database, we have to add a specific condition that injects decrypted content directly to the ndb content parser. This is done in conjunction with some minor refactoring to `NdbBlocks` and associated structs, as in C those are separated between the content string and the offsets for each block, but in Swift this is more ergonomically represented as a standalone/self-containing object. No changelog entry is added because the previously broken version was never released to the public, and therefore this fix produces no user-facing changes compared to the last released version. Changelog-None Closes: https://github.com/damus-io/damus/issues/3106 Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -154,9 +154,7 @@
|
||||
4C32B95F2A9AD44700DC3548 /* Enum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C32B94A2A9AD44700DC3548 /* Enum.swift */; };
|
||||
4C32B9602A9AD44700DC3548 /* Struct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C32B94B2A9AD44700DC3548 /* Struct.swift */; };
|
||||
4C36245B2D5E9B2F00DD066E /* NdbProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FFE2B631C0100F2B2C0 /* NdbProfile.swift */; };
|
||||
4C36245C2D5E9B4400DD066E /* NdbBlocksIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */; };
|
||||
4C36245D2D5E9B4400DD066E /* NdbBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480582B633F3800F2B2C0 /* NdbBlock.swift */; };
|
||||
4C36245E2D5E9B5F00DD066E /* NdbBlocksIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */; };
|
||||
4C36245F2D5E9B5F00DD066E /* NdbBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480582B633F3800F2B2C0 /* NdbBlock.swift */; };
|
||||
4C3624602D5E9EB800DD066E /* NdbProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FFE2B631C0100F2B2C0 /* NdbProfile.swift */; };
|
||||
4C3624612D5E9FFD00DD066E /* wasm.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480532B631C4F00F2B2C0 /* wasm.c */; };
|
||||
@@ -343,10 +341,8 @@
|
||||
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883B5297730E400DC99E7 /* LNUrls.swift */; };
|
||||
4CB8FC232A41ABA800763C51 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8FC222A41ABA500763C51 /* AboutView.swift */; };
|
||||
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */; };
|
||||
4CBB6F662B72B5DD000477A4 /* NdbBlocksIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */; };
|
||||
4CBB6F672B72B5E8000477A4 /* NdbBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480582B633F3800F2B2C0 /* NdbBlock.swift */; };
|
||||
4CBB6F682B72B5F0000477A4 /* NdbProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FFE2B631C0100F2B2C0 /* NdbProfile.swift */; };
|
||||
4CBB6F692B72C783000477A4 /* NdbBlocksIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */; };
|
||||
4CBB6F6A2B730EF1000477A4 /* nostrdb.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FDE2B631C0100F2B2C0 /* nostrdb.c */; };
|
||||
4CBB6F6E2B731113000477A4 /* block.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FDF2B631C0100F2B2C0 /* block.c */; };
|
||||
4CBB6F6F2B73116B000477A4 /* content_parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FF62B631C0100F2B2C0 /* content_parser.c */; };
|
||||
@@ -1558,15 +1554,15 @@
|
||||
D74AAFD22B155E78006CF0F4 /* WalletConnect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7D09612A098D0E00943473 /* WalletConnect.swift */; };
|
||||
D74AAFD42B155ECB006CF0F4 /* Zaps+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74AAFD32B155ECB006CF0F4 /* Zaps+.swift */; };
|
||||
D74AAFD62B155F0C006CF0F4 /* WalletConnect+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74AAFD52B155F0C006CF0F4 /* WalletConnect+.swift */; };
|
||||
D74E64132DC95CC7004C7892 /* HumanReadableErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74E64112DC95CBE004C7892 /* HumanReadableErrors.swift */; };
|
||||
D74E64142DC95CC7004C7892 /* HumanReadableErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74E64112DC95CBE004C7892 /* HumanReadableErrors.swift */; };
|
||||
D74E64152DC95CC7004C7892 /* HumanReadableErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74E64112DC95CBE004C7892 /* HumanReadableErrors.swift */; };
|
||||
D74DEC8A2DA0A19B00E69FA6 /* Ndb+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74DEC892DA0A19800E69FA6 /* Ndb+.swift */; };
|
||||
D74DEC8B2DA0A19B00E69FA6 /* Ndb+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74DEC892DA0A19800E69FA6 /* Ndb+.swift */; };
|
||||
D74DEC8C2DA0A19B00E69FA6 /* Ndb+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74DEC892DA0A19800E69FA6 /* Ndb+.swift */; };
|
||||
D74DEC8F2DA0C65F00E69FA6 /* Ndb+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74DEC892DA0A19800E69FA6 /* Ndb+.swift */; };
|
||||
D74DEC902DA0C6B500E69FA6 /* NostrFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFAE28049D340006080F /* NostrFilter.swift */; };
|
||||
D74DEC912DA0CA2400E69FA6 /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72E12772BEED22400F4F781 /* Array.swift */; };
|
||||
D74E64132DC95CC7004C7892 /* HumanReadableErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74E64112DC95CBE004C7892 /* HumanReadableErrors.swift */; };
|
||||
D74E64142DC95CC7004C7892 /* HumanReadableErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74E64112DC95CBE004C7892 /* HumanReadableErrors.swift */; };
|
||||
D74E64152DC95CC7004C7892 /* HumanReadableErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74E64112DC95CBE004C7892 /* HumanReadableErrors.swift */; };
|
||||
D74EA08A2D2BF2A7002290DD /* URLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D767066E2C8BB3CE00F09726 /* URLHandler.swift */; };
|
||||
D74EA08E2D2E271E002290DD /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74EA08D2D2E271E002290DD /* ErrorView.swift */; };
|
||||
D74EA08F2D2E271E002290DD /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74EA08D2D2E271E002290DD /* ErrorView.swift */; };
|
||||
@@ -1782,13 +1778,13 @@
|
||||
D7F360262CEBBD8B009D34DA /* PresentFullScreenItemNotify.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EB00AF2CD59C8300660C07 /* PresentFullScreenItemNotify.swift */; };
|
||||
D7F360272CEBBDC0009D34DA /* DamusVideoControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EFBA362CC322F300F45588 /* DamusVideoControlsView.swift */; };
|
||||
D7F360292CEBBE34009D34DA /* CodeScanner in Frameworks */ = {isa = PBXBuildFile; productRef = D7F360282CEBBE34009D34DA /* CodeScanner */; };
|
||||
D7FA46E52DBDAA7E002C9BB0 /* ImageCacheMigrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FA46E42DBDAA75002C9BB0 /* ImageCacheMigrations.swift */; };
|
||||
D7FA46E62DBDAA7E002C9BB0 /* ImageCacheMigrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FA46E42DBDAA75002C9BB0 /* ImageCacheMigrations.swift */; };
|
||||
D7FA46E72DBDAA7E002C9BB0 /* ImageCacheMigrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FA46E42DBDAA75002C9BB0 /* ImageCacheMigrations.swift */; };
|
||||
D7F563102DEE71C0008509DE /* NdbFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5630F2DEE71BB008509DE /* NdbFilter.swift */; };
|
||||
D7F563112DEE71C0008509DE /* NdbFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5630F2DEE71BB008509DE /* NdbFilter.swift */; };
|
||||
D7F563122DEE71C0008509DE /* NdbFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5630F2DEE71BB008509DE /* NdbFilter.swift */; };
|
||||
D7F563132DEE71C0008509DE /* NdbFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5630F2DEE71BB008509DE /* NdbFilter.swift */; };
|
||||
D7FA46E52DBDAA7E002C9BB0 /* ImageCacheMigrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FA46E42DBDAA75002C9BB0 /* ImageCacheMigrations.swift */; };
|
||||
D7FA46E62DBDAA7E002C9BB0 /* ImageCacheMigrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FA46E42DBDAA75002C9BB0 /* ImageCacheMigrations.swift */; };
|
||||
D7FA46E72DBDAA7E002C9BB0 /* ImageCacheMigrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FA46E42DBDAA75002C9BB0 /* ImageCacheMigrations.swift */; };
|
||||
D7FB10A72B0C371A00FA8D42 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2B10272A7B0F5C008AA43E /* Log.swift */; };
|
||||
D7FB14222BE5970000398331 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = D7FB14212BE5970000398331 /* PrivacyInfo.xcprivacy */; };
|
||||
D7FB14252BE5A9A800398331 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = D7FB14242BE5A9A800398331 /* PrivacyInfo.xcprivacy */; };
|
||||
@@ -2484,7 +2480,6 @@
|
||||
4CF480372B631C0100F2B2C0 /* invoice.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = invoice.c; sourceTree = "<group>"; };
|
||||
4CF480532B631C4F00F2B2C0 /* wasm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wasm.c; sourceTree = "<group>"; };
|
||||
4CF480542B631C4F00F2B2C0 /* wasm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wasm.h; sourceTree = "<group>"; };
|
||||
4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbBlocksIterator.swift; sourceTree = "<group>"; };
|
||||
4CF480582B633F3800F2B2C0 /* NdbBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbBlock.swift; sourceTree = "<group>"; };
|
||||
4CFD502E2A2DA45800A229DB /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
|
||||
4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleView.swift; sourceTree = "<group>"; };
|
||||
@@ -2628,8 +2623,8 @@
|
||||
D74AAFCE2B155D8C006CF0F4 /* ZapDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapDataModel.swift; sourceTree = "<group>"; };
|
||||
D74AAFD32B155ECB006CF0F4 /* Zaps+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Zaps+.swift"; sourceTree = "<group>"; };
|
||||
D74AAFD52B155F0C006CF0F4 /* WalletConnect+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WalletConnect+.swift"; sourceTree = "<group>"; };
|
||||
D74E64112DC95CBE004C7892 /* HumanReadableErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HumanReadableErrors.swift; sourceTree = "<group>"; };
|
||||
D74DEC892DA0A19800E69FA6 /* Ndb+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ndb+.swift"; sourceTree = "<group>"; };
|
||||
D74E64112DC95CBE004C7892 /* HumanReadableErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HumanReadableErrors.swift; sourceTree = "<group>"; };
|
||||
D74EA08D2D2E271E002290DD /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
|
||||
D74EA0922D2E77B9002290DD /* LoadableNostrEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableNostrEventView.swift; sourceTree = "<group>"; };
|
||||
D74F43092B23F0BE00425B75 /* DamusPurple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurple.swift; sourceTree = "<group>"; };
|
||||
@@ -2694,8 +2689,8 @@
|
||||
D7EDED2D2B128E8A0018B19C /* CollectionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionExtension.swift; sourceTree = "<group>"; };
|
||||
D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusUserDefaults.swift; sourceTree = "<group>"; };
|
||||
D7EFBA362CC322F300F45588 /* DamusVideoControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoControlsView.swift; sourceTree = "<group>"; };
|
||||
D7FA46E42DBDAA75002C9BB0 /* ImageCacheMigrations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCacheMigrations.swift; sourceTree = "<group>"; };
|
||||
D7F5630F2DEE71BB008509DE /* NdbFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbFilter.swift; sourceTree = "<group>"; };
|
||||
D7FA46E42DBDAA75002C9BB0 /* ImageCacheMigrations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCacheMigrations.swift; sourceTree = "<group>"; };
|
||||
D7FB14212BE5970000398331 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
D7FB14242BE5A9A800398331 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstAidSettingsView.swift; sourceTree = "<group>"; };
|
||||
@@ -3183,7 +3178,6 @@
|
||||
4C78EFDA2A707C67007E8197 /* secp256k1_extrakeys.h */,
|
||||
4C78EFD92A707C4D007E8197 /* secp256k1.h */,
|
||||
D798D2272B085CDA00234419 /* NdbNote+.swift */,
|
||||
4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */,
|
||||
4CF480582B633F3800F2B2C0 /* NdbBlock.swift */,
|
||||
);
|
||||
path = nostrdb;
|
||||
@@ -5384,7 +5378,6 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4CBB6F692B72C783000477A4 /* NdbBlocksIterator.swift in Sources */,
|
||||
4C3DCC762A9FE9EC0091E592 /* NdbTxn.swift in Sources */,
|
||||
4CEF958D2A9CE650000F901B /* verifier.c in Sources */,
|
||||
D73BDB0E2D6FF5F600D69970 /* NostrNetworkManager.swift in Sources */,
|
||||
@@ -5998,7 +5991,6 @@
|
||||
4C3624632D5EA01100DD066E /* block.c in Sources */,
|
||||
4C3624622D5EA00300DD066E /* nostrdb.c in Sources */,
|
||||
4C3624612D5E9FFD00DD066E /* wasm.c in Sources */,
|
||||
4C36245C2D5E9B4400DD066E /* NdbBlocksIterator.swift in Sources */,
|
||||
4C36245D2D5E9B4400DD066E /* NdbBlock.swift in Sources */,
|
||||
4C36245B2D5E9B2F00DD066E /* NdbProfile.swift in Sources */,
|
||||
D7F360262CEBBD8B009D34DA /* PresentFullScreenItemNotify.swift in Sources */,
|
||||
@@ -6508,7 +6500,6 @@
|
||||
4C3624742D5EA1D700DD066E /* wasm.c in Sources */,
|
||||
4C3624732D5EA1BE00DD066E /* nostrdb.c in Sources */,
|
||||
4C3624602D5E9EB800DD066E /* NdbProfile.swift in Sources */,
|
||||
4C36245E2D5E9B5F00DD066E /* NdbBlocksIterator.swift in Sources */,
|
||||
4C36245F2D5E9B5F00DD066E /* NdbBlock.swift in Sources */,
|
||||
D73E5E202C6A97F4007EB227 /* AttachedWalletNotify.swift in Sources */,
|
||||
D73E5E212C6A97F4007EB227 /* DisplayTabBarNotify.swift in Sources */,
|
||||
@@ -7035,7 +7026,6 @@
|
||||
4CBB6F6A2B730EF1000477A4 /* nostrdb.c in Sources */,
|
||||
4CBB6F682B72B5F0000477A4 /* NdbProfile.swift in Sources */,
|
||||
4CBB6F672B72B5E8000477A4 /* NdbBlock.swift in Sources */,
|
||||
4CBB6F662B72B5DD000477A4 /* NdbBlocksIterator.swift in Sources */,
|
||||
D74DEC8F2DA0C65F00E69FA6 /* Ndb+.swift in Sources */,
|
||||
D798D21F2B0858D600234419 /* MigratedTypes.swift in Sources */,
|
||||
D7CE1B472B0BE719002EDAD4 /* NativeObject.swift in Sources */,
|
||||
|
||||
@@ -780,12 +780,11 @@ func validate_event(ev: NostrEvent) -> ValidationResult {
|
||||
}
|
||||
|
||||
func first_eref_mention(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> Mention<NoteId>? {
|
||||
guard let blocks_txn = ev.blocks(ndb: ndb) else {
|
||||
guard let blockGroup = try? NdbBlockGroup.from(event: ev, using: ndb, and: keypair) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let ndb_blocks = blocks_txn.unsafeUnownedValue
|
||||
let blocks = ndb_blocks.iter(note: ev).filter { block in
|
||||
let blocks = blockGroup.blocks.filter { block in
|
||||
guard case .mention(let mention) = block else {
|
||||
return false
|
||||
}
|
||||
@@ -815,12 +814,11 @@ func first_eref_mention(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> Mention<N
|
||||
return nil
|
||||
}
|
||||
|
||||
func separate_invoices(ndb: Ndb, ev: NostrEvent) -> [Invoice]? {
|
||||
guard let blocks_txn = ev.blocks(ndb: ndb) else {
|
||||
func separate_invoices(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> [Invoice]? {
|
||||
guard let blockGroup = try? NdbBlockGroup.from(event: ev, using: ndb, and: keypair) else {
|
||||
return nil
|
||||
}
|
||||
let ndb_blocks = blocks_txn.unsafeUnownedValue
|
||||
let invoiceBlocks: [Invoice] = ndb_blocks.iter(note: ev).reduce(into: []) { invoices, block in
|
||||
let invoiceBlocks: [Invoice] = blockGroup.blocks.reduce(into: []) { invoices, block in
|
||||
guard case .invoice(let invoice) = block,
|
||||
let invoice = invoice.as_invoice()
|
||||
else {
|
||||
|
||||
@@ -67,22 +67,28 @@ func note_artifact_is_separated(kind: NostrKind?) -> Bool {
|
||||
}
|
||||
|
||||
func render_immediately_available_note_content(ndb: Ndb, ev: NostrEvent, profiles: Profiles, keypair: Keypair) -> NoteArtifacts {
|
||||
guard let blocks = ev.blocks(ndb: ndb) else {
|
||||
return .separated(.just_content(ev.get_content(keypair)))
|
||||
}
|
||||
|
||||
if ev.known_kind == .longform {
|
||||
return .longform(LongformContent(ev.content))
|
||||
}
|
||||
|
||||
return .separated(render_blocks(blocks: blocks.unsafeUnownedValue, profiles: profiles, note: ev, can_hide_last_previewable_refs: true))
|
||||
do {
|
||||
let blocks = try NdbBlockGroup.from(event: ev, using: ndb, and: keypair)
|
||||
return .separated(render_blocks(blocks: blocks, profiles: profiles, can_hide_last_previewable_refs: true))
|
||||
}
|
||||
catch {
|
||||
// TODO: Improve error handling in the future, bubbling it up so that the view can decide how display errors. Keep legacy behavior for now.
|
||||
return .separated(.just_content(ev.get_content(keypair)))
|
||||
}
|
||||
}
|
||||
|
||||
actor ContentRenderer {
|
||||
func render_note_content(ndb: Ndb, ev: NostrEvent, profiles: Profiles, keypair: Keypair) async -> NoteArtifacts {
|
||||
guard let result = try? await ndb.waitFor(noteId: ev.id, timeout: 10) else {
|
||||
return .separated(.just_content(ev.get_content(keypair)))
|
||||
if ev.known_kind == .dm {
|
||||
// Use the enhanced render_immediately_available_note_content which now handles DMs properly
|
||||
// by decrypting and parsing the content with ndb_parse_content
|
||||
return render_immediately_available_note_content(ndb: ndb, ev: ev, profiles: profiles, keypair: keypair)
|
||||
}
|
||||
let result = try? await ndb.waitFor(noteId: ev.id, timeout: 3)
|
||||
return render_immediately_available_note_content(ndb: ndb, ev: ev, profiles: profiles, keypair: keypair)
|
||||
}
|
||||
}
|
||||
@@ -92,14 +98,14 @@ actor ContentRenderer {
|
||||
// Block previews should actually be rendered in the position of the note content where it was found.
|
||||
// Currently, we put some previews at the bottom of the note, which is incorrect as they take things out of
|
||||
// the author's intended context.
|
||||
func render_blocks(blocks: NdbBlocks, profiles: Profiles, note: NdbNote, can_hide_last_previewable_refs: Bool = false) -> NoteArtifactsSeparated {
|
||||
func render_blocks(blocks: borrowing NdbBlockGroup, profiles: Profiles, can_hide_last_previewable_refs: Bool = false) -> NoteArtifactsSeparated {
|
||||
var invoices: [Invoice] = []
|
||||
var urls: [UrlType] = []
|
||||
|
||||
var end_mention_count = 0
|
||||
var end_url_count = 0
|
||||
|
||||
let ndb_blocks = blocks.iter(note: note).collect()
|
||||
let ndb_blocks = blocks.blocks
|
||||
let one_note_ref = ndb_blocks
|
||||
.filter({
|
||||
if case .mention(let mention) = $0,
|
||||
|
||||
@@ -350,11 +350,10 @@ struct NoteContentView: View {
|
||||
var body: some View {
|
||||
ArtifactContent
|
||||
.onReceive(handle_notify(.profile_updated)) { profile in
|
||||
guard let blocks_txn = event.blocks(ndb: damus_state.ndb) else {
|
||||
guard let blockGroup = try? NdbBlockGroup.from(event: event, using: damus_state.ndb, and: damus_state.keypair) else {
|
||||
return
|
||||
}
|
||||
let blocks = blocks_txn.unsafeUnownedValue
|
||||
for block in blocks.iter(note: event) {
|
||||
for block in blockGroup.blocks {
|
||||
switch block {
|
||||
case .mention(let m):
|
||||
guard let typ = m.bech32_type else {
|
||||
@@ -536,11 +535,10 @@ struct NoteContentView_Previews: PreviewProvider {
|
||||
}
|
||||
|
||||
func separate_images(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> [MediaUrl]? {
|
||||
guard let blocks_txn = ev.blocks(ndb: ndb) else {
|
||||
guard let blockGroup = try? NdbBlockGroup.from(event: ev, using: ndb, and: keypair) else {
|
||||
return nil
|
||||
}
|
||||
let blocks = blocks_txn.unsafeUnownedValue
|
||||
let urlBlocks: [URL] = blocks.iter(note: ev).reduce(into: []) { urls, block in
|
||||
let urlBlocks: [URL] = blockGroup.blocks.reduce(into: []) { urls, block in
|
||||
guard case .url(let url) = block,
|
||||
let parsed_url = URL(string: url.as_str()) else {
|
||||
return
|
||||
|
||||
@@ -72,9 +72,9 @@ func generate_local_notification_object(ndb: Ndb, from ev: NostrEvent, state: He
|
||||
|
||||
if type == .text,
|
||||
state.settings.mention_notification,
|
||||
let blocks = ev.blocks(ndb: ndb)?.unsafeUnownedValue
|
||||
let blockGroup = try? NdbBlockGroup.from(event: ev, using: ndb, and: state.keypair)
|
||||
{
|
||||
for case .mention(let mention) in blocks.iter(note: ev) {
|
||||
for case .mention(let mention) in blockGroup.blocks {
|
||||
guard case .npub = mention.bech32_type,
|
||||
(memcmp(state.keypair.pubkey.id.bytes, mention.bech32.npub.pubkey, 32) == 0) else {
|
||||
continue
|
||||
|
||||
@@ -342,6 +342,20 @@ class NoteContentViewTests: XCTestCase {
|
||||
XCTAssertTrue((parsed.blocks[0].asURL != nil), "NoteContentView does not correctly parse an image block when url in JSON content contains optional escaped slashes.")
|
||||
}
|
||||
|
||||
/// Quick test that exercises the direct parsing methods (i.e. not fetching blocks from nostrdb) from `NdbBlockGroup`, and its bridging code with C.
|
||||
/// The parsing logic itself already has test coverage at the nostrdb level.
|
||||
func testDirectBlockParsing() {
|
||||
let kp = test_keypair_full
|
||||
let dm: NdbNote = NIP04.create_dm("Test", to_pk: kp.pubkey, tags: [], keypair: kp.to_keypair())!
|
||||
let blocks = try! NdbBlockGroup.from(event: dm, using: test_damus_state.ndb, and: kp.to_keypair())
|
||||
XCTAssertEqual(blocks.blocks.count, 1)
|
||||
|
||||
let post = NostrPost(content: "Test", kind: .text)
|
||||
let event = post.to_event(keypair: kp)!
|
||||
let blocks2 = try! NdbBlockGroup.from(event: event, using: test_damus_state.ndb, and: kp.to_keypair())
|
||||
XCTAssertEqual(blocks2.blocks.count, 1)
|
||||
}
|
||||
|
||||
func testMentionStr_Pubkey_ContainsAbbreviated() throws {
|
||||
let compatibleText = createCompatibleText(test_pubkey.npub)
|
||||
|
||||
|
||||
@@ -227,16 +227,16 @@ class Ndb {
|
||||
return true
|
||||
}
|
||||
|
||||
func lookup_blocks_by_key_with_txn<Y>(_ key: NoteKey, txn: NdbTxn<Y>) -> NdbBlocks? {
|
||||
func lookup_blocks_by_key_with_txn(_ key: NoteKey, txn: RawNdbTxnAccessible) -> NdbBlockGroup.BlocksMetadata? {
|
||||
guard let blocks = ndb_get_blocks_by_key(self.ndb.ndb, &txn.txn, key) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NdbBlocks(ptr: blocks)
|
||||
return NdbBlockGroup.BlocksMetadata(ptr: blocks)
|
||||
}
|
||||
|
||||
func lookup_blocks_by_key(_ key: NoteKey) -> NdbTxn<NdbBlocks?>? {
|
||||
NdbTxn(ndb: self) { txn in
|
||||
func lookup_blocks_by_key(_ key: NoteKey) -> SafeNdbTxn<NdbBlockGroup.BlocksMetadata?>? {
|
||||
SafeNdbTxn<NdbBlockGroup.BlocksMetadata?>.new(on: self) { txn in
|
||||
lookup_blocks_by_key_with_txn(key, txn: txn)
|
||||
}
|
||||
}
|
||||
@@ -493,7 +493,7 @@ class Ndb {
|
||||
}
|
||||
}
|
||||
|
||||
func lookup_note_key_with_txn<Y>(_ id: NoteId, txn: NdbTxn<Y>) -> NoteKey? {
|
||||
func lookup_note_key_with_txn(_ id: NoteId, txn: some RawNdbTxnAccessible) -> NoteKey? {
|
||||
guard !closed else { return nil }
|
||||
return id.id.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> NoteKey? in
|
||||
guard let p = ptr.baseAddress else {
|
||||
|
||||
@@ -93,31 +93,180 @@ enum NdbBlock {
|
||||
guard let cString = block.str else {
|
||||
return nil
|
||||
}
|
||||
// Copy byte-by-byte from the pointer into a new buffer
|
||||
let byteBuffer = UnsafeBufferPointer(start: cString, count: Int(block.len)).map { UInt8(bitPattern: $0) }
|
||||
|
||||
// Create a Swift String from the byte array
|
||||
// Create an owned Swift String from the buffer we created
|
||||
return String(bytes: byteBuffer, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct NdbBlocks {
|
||||
private let blocks_ptr: ndb_blocks_ptr
|
||||
|
||||
init(ptr: OpaquePointer?) {
|
||||
self.blocks_ptr = ndb_blocks_ptr(ptr: ptr)
|
||||
/// Represents a group of blocks
|
||||
struct NdbBlockGroup: ~Copyable {
|
||||
/// The block offsets
|
||||
fileprivate let metadata: MaybeTxn<BlocksMetadata>
|
||||
/// The raw text content of the note
|
||||
fileprivate let rawTextContent: String
|
||||
/// An iterable list of blocks that make up this object
|
||||
var blocks: [NdbBlock] {
|
||||
return self.collectBlocks()
|
||||
}
|
||||
|
||||
var words: Int {
|
||||
Int(ndb_blocks_word_count(blocks_ptr.ptr))
|
||||
return metadata.borrow { $0.words }
|
||||
}
|
||||
|
||||
func iter(note: NdbNote) -> BlocksSequence {
|
||||
BlocksSequence(note: note, blocks: self)
|
||||
|
||||
/// Gets the parsed blocks from a specific note.
|
||||
///
|
||||
/// This function will:
|
||||
/// - fetch blocks information from NostrDB if possible _and_ available, or
|
||||
/// - parse blocks on-demand.
|
||||
static func from(event: NdbNote, using ndb: Ndb, and keypair: Keypair) throws(NdbBlocksError) -> Self {
|
||||
if event.is_content_encrypted() {
|
||||
return try parse(event: event, keypair: keypair)
|
||||
}
|
||||
else {
|
||||
guard let offsets = event.block_offsets(ndb: ndb) else {
|
||||
return try parse(event: event, keypair: keypair)
|
||||
}
|
||||
return .init(metadata: .txn(offsets), rawTextContent: event.content)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the note contents on-demand from a specific note.
|
||||
///
|
||||
/// Prioritize using `from(event: NdbNote, using ndb: Ndb, and keypair: Keypair)` when possible.
|
||||
static func parse(event: NdbNote, keypair: Keypair) throws(NdbBlocksError) -> Self {
|
||||
guard let content = event.maybe_get_content(keypair) else { throw NdbBlocksError.decryptionError }
|
||||
guard let metadata = BlocksMetadata.parseContent(content: content) else { throw NdbBlocksError.parseError }
|
||||
return self.init(
|
||||
metadata: .pure(metadata),
|
||||
rawTextContent: content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func as_ptr() -> OpaquePointer? {
|
||||
return self.blocks_ptr.ptr
|
||||
enum MaybeTxn<T: ~Copyable>: ~Copyable {
|
||||
case pure(T)
|
||||
case txn(SafeNdbTxn<T>)
|
||||
|
||||
func borrow<Y>(_ borrowFunction: (borrowing T) -> Y) -> Y {
|
||||
switch self {
|
||||
case .pure(let item):
|
||||
return borrowFunction(item)
|
||||
case .txn(let txn):
|
||||
return borrowFunction(txn.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Helper structs
|
||||
|
||||
extension NdbBlockGroup {
|
||||
/// Wrapper for the `ndb_blocks` C struct
|
||||
///
|
||||
/// This does not store the actual block contents, only the offsets on the content string and block metadata.
|
||||
///
|
||||
/// **Implementation note:** This would be better as `~Copyable`, but `NdbTxn` does not support `~Copyable` yet.
|
||||
struct BlocksMetadata: ~Copyable {
|
||||
private let blocks_ptr: ndb_blocks_ptr
|
||||
private let buffer: UnsafeMutableRawPointer?
|
||||
|
||||
init(ptr: OpaquePointer?, buffer: UnsafeMutableRawPointer? = nil) {
|
||||
self.blocks_ptr = ndb_blocks_ptr(ptr: ptr)
|
||||
self.buffer = buffer
|
||||
}
|
||||
|
||||
var words: Int {
|
||||
Int(ndb_blocks_word_count(blocks_ptr.ptr))
|
||||
}
|
||||
|
||||
/// Gets the opaque pointer
|
||||
///
|
||||
/// **Implementation note:** This is marked `fileprivate` because we want to minimize the exposure of raw pointers to Swift code outside these wrapper structs.
|
||||
fileprivate func as_ptr() -> OpaquePointer? {
|
||||
return self.blocks_ptr.ptr
|
||||
}
|
||||
|
||||
/// Parses text content and returns the parsed block metadata if successful
|
||||
///
|
||||
/// **Implementation notes:** This is `fileprivate` because it makes no sense for outside Swift code to use this directly. Use `NdbBlockGroup` instead.
|
||||
fileprivate static func parseContent(content: String) -> Self? {
|
||||
// Allocate scratch buffer with enough space
|
||||
guard let buffer = malloc(MAX_NOTE_SIZE) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var blocks: OpaquePointer? = nil
|
||||
|
||||
// Call the C parsing function and check its success status
|
||||
let success = content.withCString { contentPtr -> Bool in
|
||||
let contentLen = content.utf8.count
|
||||
return ndb_parse_content(
|
||||
buffer.assumingMemoryBound(to: UInt8.self),
|
||||
Int32(MAX_NOTE_SIZE),
|
||||
contentPtr,
|
||||
Int32(contentLen),
|
||||
&blocks
|
||||
) == 1
|
||||
}
|
||||
|
||||
if !success || blocks == nil {
|
||||
// Something failed
|
||||
free(buffer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: We should set the owned flag as in the C code.
|
||||
// However, There does not seem to be a way to set this from Swift code. The code shown below does not work.
|
||||
// blocks!.pointee.flags |= NDB_BLOCK_FLAG_OWNED
|
||||
// But perhaps this is not necessary because `NdbBlockGroup` is non-copyable
|
||||
|
||||
return BlocksMetadata(ptr: blocks, buffer: buffer)
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let buffer {
|
||||
free(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Models specific errors that may happen when parsing or constructing an `NdbBlocks` object
|
||||
enum NdbBlocksError: Error {
|
||||
case parseError
|
||||
case decryptionError
|
||||
}
|
||||
}
|
||||
|
||||
extension NdbBlockGroup {
|
||||
/// Collects all blocks in the group into an array without using Iterator/Sequence protocols
|
||||
///
|
||||
/// **Implementation note:**
|
||||
/// This is done as a function instead of using `Sequence` and `Iterator` protocols because it does seem to be possible to conform to both `Sequence` and `~Copyable` at the same time.
|
||||
///
|
||||
/// - Returns: An array of all blocks in the group
|
||||
fileprivate func collectBlocks() -> [NdbBlock] {
|
||||
var blocks = [NdbBlock]()
|
||||
|
||||
// Ensure the C string remains valid for the entire operation by keeping
|
||||
// all operations using it within the withCString closure
|
||||
self.rawTextContent.withCString { cptr in
|
||||
var iter = ndb_block_iterator(content: cptr, blocks: nil, block: ndb_block(), p: nil)
|
||||
|
||||
// Start the iteration
|
||||
self.metadata.borrow { value in
|
||||
ndb_blocks_iterate_start(cptr, value.as_ptr(), &iter)
|
||||
}
|
||||
|
||||
// Collect blocks into array
|
||||
while let ptr = ndb_blocks_iterate_next(&iter),
|
||||
let block = NdbBlock(ndb_block_ptr(ptr: ptr)) {
|
||||
blocks.append(block)
|
||||
}
|
||||
}
|
||||
|
||||
return blocks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// NdbBlockIterator.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2024-01-25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
struct BlocksIterator: IteratorProtocol {
|
||||
typealias Element = NdbBlock
|
||||
|
||||
var done: Bool
|
||||
var iter: ndb_block_iterator
|
||||
var note: NdbNote
|
||||
|
||||
mutating func next() -> NdbBlock? {
|
||||
guard iter.blocks != nil,
|
||||
let ptr = ndb_blocks_iterate_next(&iter) else {
|
||||
done = true
|
||||
return nil
|
||||
}
|
||||
|
||||
let block_ptr = ndb_block_ptr(ptr: ptr)
|
||||
return NdbBlock(block_ptr)
|
||||
}
|
||||
|
||||
init(note: NdbNote, blocks: NdbBlocks) {
|
||||
let content = ndb_note_content(note.note.ptr)
|
||||
self.iter = ndb_block_iterator(content: content, blocks: nil, block: ndb_block(), p: nil)
|
||||
ndb_blocks_iterate_start(content, blocks.as_ptr(), &self.iter)
|
||||
self.done = false
|
||||
self.note = note
|
||||
}
|
||||
}
|
||||
|
||||
struct BlocksSequence: Sequence {
|
||||
let blocks: NdbBlocks
|
||||
let note: NdbNote
|
||||
|
||||
init(note: NdbNote, blocks: NdbBlocks) {
|
||||
self.blocks = blocks
|
||||
self.note = note
|
||||
}
|
||||
|
||||
func makeIterator() -> BlocksIterator {
|
||||
return .init(note: note, blocks: blocks)
|
||||
}
|
||||
|
||||
func collect() -> [NdbBlock] {
|
||||
var xs = [NdbBlock]()
|
||||
for x in self {
|
||||
xs.append(x)
|
||||
}
|
||||
return xs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,23 +457,25 @@ extension NdbNote {
|
||||
return ThreadReply(tags: self.tags)?.reply.note_id
|
||||
}
|
||||
|
||||
func blocks(ndb: Ndb) -> NdbTxn<NdbBlocks>? {
|
||||
let blocks_txn = NdbTxn<NdbBlocks?>(ndb: ndb) { txn in
|
||||
func block_offsets(ndb: Ndb) -> SafeNdbTxn<NdbBlockGroup.BlocksMetadata>? {
|
||||
let blocks_txn: SafeNdbTxn<NdbBlockGroup.BlocksMetadata>? = .new(on: ndb) { txn -> NdbBlockGroup.BlocksMetadata? in
|
||||
guard let key = ndb.lookup_note_key_with_txn(self.id, txn: txn) else {
|
||||
return nil
|
||||
}
|
||||
return ndb.lookup_blocks_by_key_with_txn(key, txn: txn)
|
||||
}
|
||||
|
||||
guard let blocks_txn else {
|
||||
return nil
|
||||
}
|
||||
guard let blocks_txn else { return nil }
|
||||
|
||||
return blocks_txn.collect()
|
||||
return blocks_txn
|
||||
}
|
||||
|
||||
func is_content_encrypted() -> Bool {
|
||||
return known_kind == .dm // Probably other kinds should be listed here
|
||||
}
|
||||
|
||||
func get_content(_ keypair: Keypair) -> String {
|
||||
if known_kind == .dm {
|
||||
if is_content_encrypted() {
|
||||
return decrypted(keypair: keypair) ?? "*failed to decrypt content*"
|
||||
}
|
||||
else if known_kind == .highlight {
|
||||
@@ -484,7 +486,7 @@ extension NdbNote {
|
||||
}
|
||||
|
||||
func maybe_get_content(_ keypair: Keypair) -> String? {
|
||||
if known_kind == .dm {
|
||||
if is_content_encrypted() {
|
||||
return decrypted(keypair: keypair)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ fileprivate var txn_count: Int = 0
|
||||
#endif
|
||||
|
||||
// Would use struct and ~Copyable but generics aren't supported well
|
||||
class NdbTxn<T> {
|
||||
class NdbTxn<T>: RawNdbTxnAccessible {
|
||||
var txn: ndb_txn
|
||||
private var val: T!
|
||||
var moved: Bool
|
||||
@@ -117,6 +117,129 @@ class NdbTxn<T> {
|
||||
}
|
||||
}
|
||||
|
||||
protocol RawNdbTxnAccessible: AnyObject {
|
||||
var txn: ndb_txn { get set }
|
||||
}
|
||||
|
||||
class PlaceholderNdbTxn: RawNdbTxnAccessible {
|
||||
var txn: ndb_txn
|
||||
|
||||
init(txn: ndb_txn) {
|
||||
self.txn = txn
|
||||
}
|
||||
}
|
||||
|
||||
class SafeNdbTxn<T: ~Copyable> {
|
||||
var txn: ndb_txn
|
||||
var val: T!
|
||||
var moved: Bool
|
||||
var inherited: Bool
|
||||
var ndb: Ndb
|
||||
var generation: Int
|
||||
var name: String
|
||||
|
||||
static func pure(ndb: Ndb, val: consuming T) -> SafeNdbTxn<T> {
|
||||
.init(ndb: ndb, txn: ndb_txn(), val: val, generation: ndb.generation, inherited: true, name: "pure_txn")
|
||||
}
|
||||
|
||||
static func new(on ndb: Ndb, with valueGetter: (PlaceholderNdbTxn) -> T? = { _ in () }, name: String = "txn") -> SafeNdbTxn<T>? {
|
||||
guard !ndb.is_closed else { return nil }
|
||||
var generation = ndb.generation
|
||||
var txn: ndb_txn
|
||||
let inherited: Bool
|
||||
if let active_txn = Thread.current.threadDictionary["ndb_txn"] as? ndb_txn {
|
||||
// some parent thread is active, use that instead
|
||||
print("txn: inherited txn")
|
||||
txn = active_txn
|
||||
inherited = true
|
||||
generation = Thread.current.threadDictionary["txn_generation"] as! Int
|
||||
} else {
|
||||
txn = ndb_txn()
|
||||
guard !ndb.is_closed else { return nil }
|
||||
generation = ndb.generation
|
||||
#if TXNDEBUG
|
||||
txn_count += 1
|
||||
#endif
|
||||
let ok = ndb_begin_query(ndb.ndb.ndb, &txn) != 0
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
generation = ndb.generation
|
||||
Thread.current.threadDictionary["ndb_txn"] = txn
|
||||
Thread.current.threadDictionary["txn_generation"] = ndb.generation
|
||||
inherited = false
|
||||
}
|
||||
#if TXNDEBUG
|
||||
print("txn: open gen\(self.generation) '\(self.name)' \(txn_count)")
|
||||
#endif
|
||||
let moved = false
|
||||
let placeholderTxn = PlaceholderNdbTxn(txn: txn)
|
||||
guard let val = valueGetter(placeholderTxn) else { return nil }
|
||||
return SafeNdbTxn<T>(ndb: ndb, txn: txn, val: val, generation: generation, inherited: inherited, name: name)
|
||||
}
|
||||
|
||||
private init(ndb: Ndb, txn: ndb_txn, val: consuming T, generation: Int, inherited: Bool, name: String) {
|
||||
self.txn = txn
|
||||
self.val = consume val
|
||||
self.moved = false
|
||||
self.inherited = inherited
|
||||
self.ndb = ndb
|
||||
self.generation = generation
|
||||
self.name = name
|
||||
}
|
||||
|
||||
deinit {
|
||||
if self.generation != ndb.generation {
|
||||
print("txn: OLD GENERATION (\(self.generation) != \(ndb.generation)), IGNORING")
|
||||
return
|
||||
}
|
||||
if inherited {
|
||||
print("txn: not closing. inherited ")
|
||||
return
|
||||
}
|
||||
if moved {
|
||||
//print("txn: not closing. moved")
|
||||
return
|
||||
}
|
||||
if ndb.is_closed {
|
||||
print("txn: not closing. db closed")
|
||||
return
|
||||
}
|
||||
|
||||
#if TXNDEBUG
|
||||
txn_count -= 1;
|
||||
print("txn: close gen\(generation) '\(name)' \(txn_count)")
|
||||
#endif
|
||||
ndb_end_query(&self.txn)
|
||||
//self.skip_close = true
|
||||
Thread.current.threadDictionary.removeObject(forKey: "ndb_txn")
|
||||
}
|
||||
|
||||
// functor
|
||||
func map<Y>(_ transform: (borrowing T) -> Y) -> SafeNdbTxn<Y> {
|
||||
self.moved = true
|
||||
return .init(ndb: self.ndb, txn: self.txn, val: transform(val), generation: generation, inherited: inherited, name: self.name)
|
||||
}
|
||||
|
||||
// comonad!?
|
||||
// useful for moving ownership of a transaction to another value
|
||||
func extend<Y>(_ with: (SafeNdbTxn<T>) -> Y) -> SafeNdbTxn<Y> {
|
||||
self.moved = true
|
||||
return .init(ndb: self.ndb, txn: self.txn, val: with(self), generation: generation, inherited: inherited, name: self.name)
|
||||
}
|
||||
|
||||
consuming func maybeExtend<Y>(_ with: (consuming SafeNdbTxn<T>) -> Y?) -> SafeNdbTxn<Y>? where Y: ~Copyable {
|
||||
self.moved = true
|
||||
let ndb = self.ndb
|
||||
let txn = self.txn
|
||||
let generation = self.generation
|
||||
let inherited = self.inherited
|
||||
let name = self.name
|
||||
guard let newVal = with(consume self) else { return nil }
|
||||
return .init(ndb: ndb, txn: txn, val: newVal, generation: generation, inherited: inherited, name: name)
|
||||
}
|
||||
}
|
||||
|
||||
protocol OptionalType {
|
||||
associatedtype Wrapped
|
||||
var optional: Wrapped? { get }
|
||||
|
||||
Reference in New Issue
Block a user