Fix issue where repost and like counts would not appear

Previously, HomeModel could listen to all subscriptions throughout the
app, and it would handle reaction and repost counting.

Once moved to the local relay model, HomeModel no longer had access to
all subscriptions, causing those counts to disappear.

The issue was fixed by doing the counting from ThreadModel itself, which
better isolates concerns throughout the app.

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2025-08-27 17:06:54 -07:00
parent 9620dcf6ef
commit ab6ea7a9c1
3 changed files with 73 additions and 0 deletions

View File

@@ -1756,6 +1756,7 @@
D7EB00B12CD59C8D00660C07 /* PresentFullScreenItemNotify.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EB00AF2CD59C8300660C07 /* PresentFullScreenItemNotify.swift */; }; D7EB00B12CD59C8D00660C07 /* PresentFullScreenItemNotify.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EB00AF2CD59C8300660C07 /* PresentFullScreenItemNotify.swift */; };
D7EBF8BB2E59022A004EAE29 /* NostrNetworkManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EBF8BA2E5901F7004EAE29 /* NostrNetworkManagerTests.swift */; }; D7EBF8BB2E59022A004EAE29 /* NostrNetworkManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EBF8BA2E5901F7004EAE29 /* NostrNetworkManagerTests.swift */; };
D7EBF8BE2E59470D004EAE29 /* test_notes.jsonl in Resources */ = {isa = PBXBuildFile; fileRef = D7EBF8BD2E594708004EAE29 /* test_notes.jsonl */; }; D7EBF8BE2E59470D004EAE29 /* test_notes.jsonl in Resources */ = {isa = PBXBuildFile; fileRef = D7EBF8BD2E594708004EAE29 /* test_notes.jsonl */; };
D7EBF8C02E5D39DC004EAE29 /* ThreadModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EBF8BF2E5D39D1004EAE29 /* ThreadModelTests.swift */; };
D7EDED152B11776B0018B19C /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; }; D7EDED152B11776B0018B19C /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; };
D7EDED162B1177840018B19C /* LNUrls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883B5297730E400DC99E7 /* LNUrls.swift */; }; D7EDED162B1177840018B19C /* LNUrls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883B5297730E400DC99E7 /* LNUrls.swift */; };
D7EDED172B1177960018B19C /* TranslationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95C9298DF87B00F3D526 /* TranslationService.swift */; }; D7EDED172B1177960018B19C /* TranslationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95C9298DF87B00F3D526 /* TranslationService.swift */; };
@@ -2692,6 +2693,7 @@
D7EB00AF2CD59C8300660C07 /* PresentFullScreenItemNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentFullScreenItemNotify.swift; sourceTree = "<group>"; }; D7EB00AF2CD59C8300660C07 /* PresentFullScreenItemNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentFullScreenItemNotify.swift; sourceTree = "<group>"; };
D7EBF8BA2E5901F7004EAE29 /* NostrNetworkManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrNetworkManagerTests.swift; sourceTree = "<group>"; }; D7EBF8BA2E5901F7004EAE29 /* NostrNetworkManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrNetworkManagerTests.swift; sourceTree = "<group>"; };
D7EBF8BD2E594708004EAE29 /* test_notes.jsonl */ = {isa = PBXFileReference; lastKnownFileType = text; path = test_notes.jsonl; sourceTree = "<group>"; }; D7EBF8BD2E594708004EAE29 /* test_notes.jsonl */ = {isa = PBXFileReference; lastKnownFileType = text; path = test_notes.jsonl; sourceTree = "<group>"; };
D7EBF8BF2E5D39D1004EAE29 /* ThreadModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModelTests.swift; sourceTree = "<group>"; };
D7EDED1B2B1178FE0018B19C /* NoteContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContent.swift; sourceTree = "<group>"; }; D7EDED1B2B1178FE0018B19C /* NoteContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContent.swift; sourceTree = "<group>"; };
D7EDED1D2B11797D0018B19C /* LongformEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformEvent.swift; sourceTree = "<group>"; }; D7EDED1D2B11797D0018B19C /* LongformEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformEvent.swift; sourceTree = "<group>"; };
D7EDED202B117DCA0018B19C /* SequenceUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceUtils.swift; sourceTree = "<group>"; }; D7EDED202B117DCA0018B19C /* SequenceUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceUtils.swift; sourceTree = "<group>"; };
@@ -5001,6 +5003,7 @@
children = ( children = (
D7EBF8BD2E594708004EAE29 /* test_notes.jsonl */, D7EBF8BD2E594708004EAE29 /* test_notes.jsonl */,
D7EBF8BA2E5901F7004EAE29 /* NostrNetworkManagerTests.swift */, D7EBF8BA2E5901F7004EAE29 /* NostrNetworkManagerTests.swift */,
D7EBF8BF2E5D39D1004EAE29 /* ThreadModelTests.swift */,
); );
path = NostrNetworkManagerTests; path = NostrNetworkManagerTests;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -5934,6 +5937,7 @@
4C9B0DEE2A65A75F00CBDA21 /* AttrStringTestExtensions.swift in Sources */, 4C9B0DEE2A65A75F00CBDA21 /* AttrStringTestExtensions.swift in Sources */,
4C19AE552A5D977400C90DB7 /* HashtagTests.swift in Sources */, 4C19AE552A5D977400C90DB7 /* HashtagTests.swift in Sources */,
D72927AD2BAB515C00F93E90 /* RelayURLTests.swift in Sources */, D72927AD2BAB515C00F93E90 /* RelayURLTests.swift in Sources */,
D7EBF8C02E5D39DC004EAE29 /* ThreadModelTests.swift in Sources */,
4C0ED07F2D7A1E260020D8A2 /* Benchmarking.swift in Sources */, 4C0ED07F2D7A1E260020D8A2 /* Benchmarking.swift in Sources */,
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */, 3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */,
D71DC1EC2A9129C3006E207C /* PostViewTests.swift in Sources */, D71DC1EC2A9129C3006E207C /* PostViewTests.swift in Sources */,

View File

@@ -190,6 +190,12 @@ class ThreadModel: ObservableObject {
self.add_event(ev, keypair: damus_state.keypair) self.add_event(ev, keypair: damus_state.keypair)
} }
} }
else if ev.known_kind == .boost {
damus_state.boosts.add_event(ev, target: original_event.id)
}
else if ev.known_kind == .like {
damus_state.likes.add_event(ev, target: original_event.id)
}
} }
// MARK: External control interface // MARK: External control interface

View File

@@ -0,0 +1,63 @@
//
// ThreadModelTests.swift
// damus
//
// Created by Daniel DAquino on 2025-08-25.
//
import XCTest
@testable import damus
final class ThreadModelTests: XCTestCase {
var damusState: DamusState? = nil
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
damusState = generate_test_damus_state(mock_profile_info: nil)
let notesJSONL = getTestNotesJSONL()
for noteText in notesJSONL.split(separator: "\n") {
let _ = damusState!.ndb.process_event("[\"EVENT\",\"subid\",\(String(noteText))]")
}
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
damusState = nil
}
func getTestNotesJSONL() -> String {
// Get the path for the test_notes.jsonl file in the same folder as this test file
let testBundle = Bundle(for: type(of: self))
let fileURL = testBundle.url(forResource: "test_notes", withExtension: "jsonl")!
// Load the contents of the file
return try! String(contentsOf: fileURL, encoding: .utf8)
}
/// Tests loading up a thread and checking if the repost count loads as expected.
func testActionBarModel() throws {
let testNoteJson = """
{"content":"https://smartflowsocial.s3.us-east-1.amazonaws.com/clients/cm7kdrwdk0000qyu6fwtd96ui/0cab65a9-0142-48e3-abd7-94d20e30d3b2.jpg\n\n","pubkey":"71ecabd8b6b33548e075ff01b31568ffda19d0ac2788067d99328c6de4885975","tags":[["t","meme"],["t","memes"],["t","memestr"],["t","plebchain"]],"created_at":1755694800,"id":"64b26d0a587f5f894470e1e4783756b4d8ba971226de975ee30ac1b69970d5a1","kind":1,"sig":"c000794da8c4f7549b546630b16ed17f6edc0af0269b8c46ce14f5b1937431e7575b78351bc152007ebab5720028e5fe4b738f99e8887f273d35dd2217d1cc3d"}
"""
let testShouldComplete = XCTestExpectation(description: "Test should complete")
Task {
let note = NostrEvent.owned_from_json(json: testNoteJson)!
let threadModel = await ThreadModel(event: note, damus_state: damusState!)
await threadModel.subscribe()
let actionBarModel = make_actionbar_model(ev: note.id, damus: damusState!)
while true {
try await Task.sleep(nanoseconds: 500_000_000)
actionBarModel.update(damus: damusState!, evid: note.id)
if actionBarModel.boosts >= 5 {
break
}
}
XCTAssertEqual(actionBarModel.boosts, 5)
testShouldComplete.fulfill()
}
wait(for: [testShouldComplete], timeout: 10.0)
}
}