diff --git a/Forji/Forji/Helpers/PaginationState.swift b/Forji/Forji/Helpers/PaginationState.swift index af74840..b5b66c2 100644 --- a/Forji/Forji/Helpers/PaginationState.swift +++ b/Forji/Forji/Helpers/PaginationState.swift @@ -9,6 +9,7 @@ final class PaginationState { private(set) var hasLoaded = false var errorMessage: String? var showError = false + var notFound = false private var currentPage = 1 private var loadTask: Task? @@ -26,10 +27,11 @@ final class PaginationState { self.pageSize = pageSize } - init(items: [Item], hasMore: Bool = false, pageSize: Int = 20) { + init(items: [Item], hasMore: Bool = false, pageSize: Int = 20, notFound: Bool = false) { self.items = items self.hasMore = hasMore self.pageSize = pageSize + self.notFound = notFound } @discardableResult @@ -44,6 +46,7 @@ final class PaginationState { hasLoaded = false isLoading = true showError = false + notFound = false let pageSize = pageSize let task = Task { do { diff --git a/Forji/Forji/Views/IssueListView.swift b/Forji/Forji/Views/IssueListView.swift index fe3763c..de17f0c 100644 --- a/Forji/Forji/Views/IssueListView.swift +++ b/Forji/Forji/Views/IssueListView.swift @@ -38,6 +38,12 @@ struct IssueListView: View { List { if pagination.isLoading, pagination.items.isEmpty { LoadingListSection() + } else if pagination.notFound { + ContentUnavailableView { + Label("Issues Unavailable", systemImage: "exclamationmark.circle") + } description: { + Text("Issues are not available for this repository.") + } } else if pagination.items.isEmpty { ContentUnavailableView { Label( @@ -116,13 +122,18 @@ struct IssueListView: View { private func reloadIssues(clearItems: Bool = false) -> Task { guard let issueService else { return Task {} } return pagination.reload(clearItems: clearItems) { [self] page, limit in - try await issueService.fetchIssues( - owner: owner, - repo: repo, - state: stateFilter.rawValue, - page: page, - limit: limit, - ) + do { + return try await issueService.fetchIssues( + owner: owner, + repo: repo, + state: stateFilter.rawValue, + page: page, + limit: limit, + ) + } catch let error as ServiceError where error.httpStatusCode == 404 { + pagination.notFound = true + return [] + } } } @@ -140,11 +151,12 @@ struct IssueListView: View { } #if DEBUG - init(preview _: Void, repository: Repository, authService: AuthenticationService, issues: [Issue]) { + init(preview _: Void, repository: Repository, authService: AuthenticationService, issues: [Issue], + notFound: Bool = false) { self.repository = repository self.authService = authService issueService = nil - _pagination = State(initialValue: PaginationState(items: issues)) + _pagination = State(initialValue: PaginationState(items: issues, notFound: notFound)) } #endif } @@ -160,6 +172,18 @@ struct IssueListView: View { ) } } + + #Preview("Issues Unavailable") { + NavigationStack { + IssueListView( + preview: (), + repository: .preview, + authService: .previewDefault, + issues: [], + notFound: true, + ) + } + } #endif struct IssueRow: View { diff --git a/Forji/ForjiTests/PaginationStateTests.swift b/Forji/ForjiTests/PaginationStateTests.swift index 32cd25f..d1dcd45 100644 --- a/Forji/ForjiTests/PaginationStateTests.swift +++ b/Forji/ForjiTests/PaginationStateTests.swift @@ -1,3 +1,4 @@ +// swiftlint:disable file_length import Foundation import Testing @testable import Forji @@ -359,6 +360,41 @@ struct PaginationStateHasLoadedTests { } } +// MARK: - notFound + +struct PaginationStateNotFoundTests { + + @Test @MainActor func notFoundFalseInitially() { + let pagination = PaginationState(pageSize: 5) + #expect(!pagination.notFound) + } + + @Test @MainActor func notFoundSetByFetchClosureShowsNoAlert() async { + let pagination = PaginationState(pageSize: 20) + await pagination.reload { _, _ in + pagination.notFound = true + return [] + }.value + #expect(pagination.notFound) + #expect(pagination.items.isEmpty) + #expect(!pagination.showError) + #expect(pagination.hasLoaded) + } + + @Test @MainActor func reloadClearsNotFound() async { + let pagination = PaginationState(pageSize: 20) + await pagination.reload { _, _ in + pagination.notFound = true + return [] + }.value + #expect(pagination.notFound) + + await pagination.reload { _, _ in ["ok"] }.value + #expect(!pagination.notFound) + #expect(pagination.items == ["ok"]) + } +} + // MARK: - loadMore struct PaginationStateLoadMoreTests { @@ -368,7 +404,7 @@ struct PaginationStateLoadMoreTests { await pagination.reload { _, _ in ["a", "b"] }.value #expect(pagination.hasMore) - await pagination.loadMore { page, limit in + await pagination.loadMore { page, _ in #expect(page == 2) return ["c", "d"] }