mirror of
https://codeberg.org/secana/Forji.git
synced 2026-06-16 05:13:55 -07:00
fix: show graceful empty state when repository has issues turned off
This commit is contained in:
commit
617e687e7b
3 changed files with 74 additions and 11 deletions
|
|
@ -9,6 +9,7 @@ final class PaginationState<Item> {
|
|||
private(set) var hasLoaded = false
|
||||
var errorMessage: String?
|
||||
var showError = false
|
||||
var notFound = false
|
||||
|
||||
private var currentPage = 1
|
||||
private var loadTask: Task<Void, Never>?
|
||||
|
|
@ -26,10 +27,11 @@ final class PaginationState<Item> {
|
|||
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<Item> {
|
|||
hasLoaded = false
|
||||
isLoading = true
|
||||
showError = false
|
||||
notFound = false
|
||||
let pageSize = pageSize
|
||||
let task = Task {
|
||||
do {
|
||||
|
|
|
|||
|
|
@ -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<Void, Never> {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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<String>(pageSize: 5)
|
||||
#expect(!pagination.notFound)
|
||||
}
|
||||
|
||||
@Test @MainActor func notFoundSetByFetchClosureShowsNoAlert() async {
|
||||
let pagination = PaginationState<String>(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<String>(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"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue