diff --git a/Packages/Models/Sources/Models/List.swift b/Packages/Models/Sources/Models/List.swift index 38e75a97..0f7a5567 100644 --- a/Packages/Models/Sources/Models/List.swift +++ b/Packages/Models/Sources/Models/List.swift @@ -13,6 +13,13 @@ public struct List: Codable, Identifiable, Equatable, Hashable { case followed, list, none } + + public init(id: String, title: String, repliesPolicy: RepliesPolicy? = nil, exclusive: Bool? = nil) { + self.id = id + self.title = title + self.repliesPolicy = repliesPolicy + self.exclusive = exclusive + } } extension List: Sendable {} diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineQuickAccessPills.swift b/Packages/Timeline/Sources/Timeline/View/TimelineQuickAccessPills.swift index 20a73206..2580e295 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineQuickAccessPills.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineQuickAccessPills.swift @@ -23,11 +23,6 @@ struct TimelineQuickAccessPills: View { } .scrollClipDisabled() .scrollIndicators(.never) - .listRowInsets(EdgeInsets(top: 8, leading: .layoutPadding, bottom: 8, trailing: .layoutPadding)) -#if !os(visionOS) - .listRowBackground(theme.primaryBackgroundColor) -#endif - .listRowSeparator(.hidden) } @ViewBuilder diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineViewModel.swift b/Packages/Timeline/Sources/Timeline/View/TimelineViewModel.swift index 59ca41b0..ba8e0865 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineViewModel.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineViewModel.swift @@ -41,7 +41,7 @@ import SwiftUI } } - private var timelineTask: Task? + private(set) var timelineTask: Task? var tag: Tag? @@ -133,8 +133,9 @@ import SwiftUI } else if let event = event as? StreamEventStatusUpdate { if let originalIndex = await datasource.indexOf(statusId: event.status.id) { await datasource.replace(event.status, at: originalIndex) + let statuses = await datasource.get() await cacheHome() - statusesState = await .display(statuses: datasource.get(), nextPageState: .hasNextPage) + statusesState = .display(statuses: statuses, nextPageState: .hasNextPage) } } } diff --git a/Packages/Timeline/Tests/TimelineTests/TimelineFilterTests.swift b/Packages/Timeline/Tests/TimelineTests/TimelineFilterTests.swift new file mode 100644 index 00000000..652e6f03 --- /dev/null +++ b/Packages/Timeline/Tests/TimelineTests/TimelineFilterTests.swift @@ -0,0 +1,25 @@ +@testable import Timeline +import XCTest +import Network +import Models + + +final class TimelineFilterTests: XCTestCase { + func testCodableHome() throws { + XCTAssertTrue(try testCodableOn(filter: .home)) + XCTAssertTrue(try testCodableOn(filter: .local)) + XCTAssertTrue(try testCodableOn(filter: .federated)) + XCTAssertTrue(try testCodableOn(filter: .remoteLocal(server: "me.dm", filter: .local))) + XCTAssertTrue(try testCodableOn(filter: .tagGroup(title: "test", tags: ["test"]))) + XCTAssertTrue(try testCodableOn(filter: .hashtag(tag: "test", accountId: nil))) + XCTAssertTrue(try testCodableOn(filter: .list(list: .init(id: "test", title: "test")))) + } + + private func testCodableOn(filter: TimelineFilter) throws -> Bool { + let encoder = JSONEncoder() + let decoder = JSONDecoder() + let data = try encoder.encode(filter) + let newFilter = try decoder.decode(TimelineFilter.self, from: data) + return newFilter == filter + } +} diff --git a/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift b/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift index 4294a9aa..5d58c754 100644 --- a/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift +++ b/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift @@ -5,13 +5,18 @@ import Models @MainActor final class TimelineViewModelTests: XCTestCase { - func testStreamEventInsertNewStatus() async throws { - let subject = TimelineViewModel() + var subject = TimelineViewModel() + + override func setUp() async throws { + subject = TimelineViewModel() let client = Client(server: "localhost") subject.client = client subject.timeline = .home subject.isTimelineVisible = true - + subject.timelineTask?.cancel() + } + + func testStreamEventInsertNewStatus() async throws { let isEmpty = await subject.datasource.isEmpty XCTAssertTrue(isEmpty) await subject.datasource.append(.placeholder()) @@ -23,12 +28,6 @@ final class TimelineViewModelTests: XCTestCase { } 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() @@ -41,12 +40,6 @@ final class TimelineViewModelTests: XCTestCase { } 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() @@ -59,12 +52,6 @@ final class TimelineViewModelTests: XCTestCase { } 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)