diff --git a/Packages/Timeline/Sources/Timeline/TimelineViewModel.swift b/Packages/Timeline/Sources/Timeline/TimelineViewModel.swift index fc15e436..59ca41b0 100644 --- a/Packages/Timeline/Sources/Timeline/TimelineViewModel.swift +++ b/Packages/Timeline/Sources/Timeline/TimelineViewModel.swift @@ -18,15 +18,7 @@ import SwiftUI didSet { timelineTask?.cancel() timelineTask = Task { - if timeline == .latest || timeline == .resume { - if oldValue == .home { - await clearHomeCache() - } - if timeline == .resume, let marker = await fetchMarker() { - self.marker = marker - } - timeline = oldValue - } + await handleLatestOrResume(oldValue) if oldValue != timeline { await reset() @@ -103,6 +95,18 @@ import SwiftUI func reset() async { await datasource.reset() } + + private func handleLatestOrResume(_ oldValue: TimelineFilter) async { + if timeline == .latest || timeline == .resume { + if oldValue == .home { + await clearHomeCache() + } + if timeline == .resume, let marker = await fetchMarker() { + self.marker = marker + } + timeline = oldValue + } + } func handleEvent(event: any StreamEvent) async { if let event = event as? StreamEventUpdate, diff --git a/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift b/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift index 7d869ab4..4294a9aa 100644 --- a/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift +++ b/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift @@ -21,4 +21,87 @@ final class TimelineViewModelTests: XCTestCase { count = await subject.datasource.count() XCTAssertTrue(count == 2) } + + func testStreamEventInsertDuplicateStatus() async throws { + let subject = TimelineViewModel() + let client = Client(server: "localhost") + subject.client = client + subject.timeline = .home + subject.isTimelineVisible = true + + let isEmpty = await subject.datasource.isEmpty + XCTAssertTrue(isEmpty) + let status = Status.placeholder() + await subject.datasource.append(status) + var count = await subject.datasource.count() + XCTAssertTrue(count == 1) + await subject.handleEvent(event: StreamEventUpdate(status: status)) + count = await subject.datasource.count() + XCTAssertTrue(count == 1) + } + + func testStreamEventRemove() async throws { + let subject = TimelineViewModel() + let client = Client(server: "localhost") + subject.client = client + subject.timeline = .home + subject.isTimelineVisible = true + + let isEmpty = await subject.datasource.isEmpty + XCTAssertTrue(isEmpty) + let status = Status.placeholder() + await subject.datasource.append(status) + var count = await subject.datasource.count() + XCTAssertTrue(count == 1) + await subject.handleEvent(event: StreamEventDelete(status: status.id)) + count = await subject.datasource.count() + XCTAssertTrue(count == 0) + } + + func testStreamEventUpdateStatus() async throws { + let subject = TimelineViewModel() + let client = Client(server: "localhost") + subject.client = client + subject.timeline = .home + subject.isTimelineVisible = true + + var status = Status.placeholder() + let isEmpty = await subject.datasource.isEmpty + XCTAssertTrue(isEmpty) + await subject.datasource.append(status) + var count = await subject.datasource.count() + XCTAssertTrue(count == 1) + status = .init(id: status.id, + content: .init(stringValue: "test"), + account: status.account, + createdAt: status.createdAt, + editedAt: status.editedAt, + reblog: status.reblog, + mediaAttachments: status.mediaAttachments, + mentions: status.mentions, + repliesCount: status.repliesCount, + reblogsCount: status.reblogsCount, + favouritesCount: status.favouritesCount, + card: status.card, + favourited: status.favourited, + reblogged: status.reblogged, + pinned: status.pinned, + bookmarked: status.bookmarked, + emojis: status.emojis, + url: status.url, + application: status.application, + inReplyToId: status.inReplyToId, + inReplyToAccountId: status.inReplyToAccountId, + visibility: status.visibility, + poll: status.poll, + spoilerText: status.spoilerText, + filtered: status.filtered, + sensitive: status.sensitive, + language: status.language) + await subject.handleEvent(event: StreamEventStatusUpdate(status: status)) + let statuses = await subject.datasource.get() + count = await subject.datasource.count() + XCTAssertTrue(count == 1) + XCTAssertTrue(statuses.first?.content.asRawText == "test") + } }