Skip to content

Commit

Permalink
Merge pull request #960 from kiwix/959-show-user-error-when-libkiwixo…
Browse files Browse the repository at this point in the history
…rg-is-not-available

Error handling for catalog not available 503
  • Loading branch information
kelson42 authored Sep 2, 2024
2 parents 2fdfacb + d27282f commit 58bfb76
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 22 deletions.
14 changes: 10 additions & 4 deletions Tests/LibraryRefreshViewModelTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ final class LibraryRefreshViewModelTest: XCTestCase {

HTTPTestingURLProtocol.handler = { urlProtocol in
let response = HTTPURLResponse(
url: URL(string: "https://library.kiwix.org/catalog/v2/entries?count=-1")!,
url: URL.mock(),
statusCode: 200, httpVersion: nil, headerFields: [:]
)!
let data = self.makeOPDSData(zimFileID: UUID()).data(using: .utf8)!
Expand Down Expand Up @@ -112,7 +112,7 @@ final class LibraryRefreshViewModelTest: XCTestCase {
func testFetchBadStatusCode() async {
HTTPTestingURLProtocol.handler = { urlProtocol in
let response = HTTPURLResponse(
url: URL(string: "https://library.kiwix.org/catalog/v2/entries?count=-1")!,
url: URL.mock(),
statusCode: 404, httpVersion: nil, headerFields: [:]
)!
urlProtocol.client?.urlProtocol(urlProtocol, didReceive: response, cacheStoragePolicy: .notAllowed)
Expand All @@ -134,7 +134,7 @@ final class LibraryRefreshViewModelTest: XCTestCase {
func testInvalidOPDSData() async {
HTTPTestingURLProtocol.handler = { urlProtocol in
let response = HTTPURLResponse(
url: URL(string: "https://library.kiwix.org/catalog/v2/entries?count=-1")!,
url: URL.mock(),
statusCode: 200, httpVersion: nil, headerFields: [:]
)!
urlProtocol.client?.urlProtocol(urlProtocol, didLoad: "Invalid OPDS Data".data(using: .utf8)!)
Expand All @@ -158,7 +158,7 @@ final class LibraryRefreshViewModelTest: XCTestCase {
let zimFileID = UUID()
HTTPTestingURLProtocol.handler = { urlProtocol in
let response = HTTPURLResponse(
url: URL(string: "https://library.kiwix.org/catalog/v2/entries?count=-1")!,
url: URL.mock(),
statusCode: 200, httpVersion: nil, headerFields: [:]
)!
let data = self.makeOPDSData(zimFileID: zimFileID).data(using: .utf8)!
Expand Down Expand Up @@ -243,3 +243,9 @@ final class LibraryRefreshViewModelTest: XCTestCase {
XCTAssertEqual(zimFiles.filter({ $0.fileID == zimFile2.fileID }).count, 1)
}
}

private extension URL {
static func mock() -> URL {
URL(string: "https://test.com")!
}
}
9 changes: 5 additions & 4 deletions ViewModel/LibraryViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum LibraryState {
case initial
case inProgress
case complete
case error

static func defaultState() -> LibraryState {
if Defaults[.libraryLastRefresh] == nil {
Expand Down Expand Up @@ -55,6 +56,8 @@ final class LibraryViewModel: ObservableObject {
private let urlSession: URLSession
private var insertionCount = 0
private var deletionCount = 0

private static let catalogURL = URL(string: "https://library.kiwix.org/catalog/v2/entries?count=-1")!

@MainActor
init(urlSession: URLSession? = nil, processFactory: @MainActor () -> LibraryProcess = { .shared }) {
Expand Down Expand Up @@ -85,7 +88,6 @@ final class LibraryViewModel: ObservableObject {

// refresh library
guard let data = try await fetchData() else {
process.state = oldState
return
}
let parser = try await parse(data: data)
Expand Down Expand Up @@ -114,7 +116,7 @@ final class LibraryViewModel: ObservableObject {
log: Log.OPDS, type: .default, insertionCount, deletionCount, parser.zimFileIDs.count)
} catch {
self.error = error
process.state = oldState
process.state = .error
}
}

Expand Down Expand Up @@ -169,9 +171,8 @@ final class LibraryViewModel: ObservableObject {
}

private func fetchData() async throws -> Data? {
guard let url = URL(string: "https://library.kiwix.org/catalog/v2/entries?count=-1") else { return nil }
do {
let request = URLRequest(url: url, timeoutInterval: 20)
let request = URLRequest(url: Self.catalogURL, timeoutInterval: 20)
let (data, response) = try await self.urlSession.data(for: request)
guard let response = response as? HTTPURLResponse else { return nil }
guard response.statusCode == 200 else {
Expand Down
10 changes: 8 additions & 2 deletions Views/BuildingBlocks/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@
import SwiftUI

struct Message: View {
let text: String
private let text: String
private let foregroundColor: Color

init(text: String, color: Color = .secondary) {
self.text = text
foregroundColor = color
}

var body: some View {
VStack {
Spacer()
HStack {
Spacer()
Text(text).font(.title2).foregroundColor(.secondary)
Text(text).font(.title2).foregroundColor(foregroundColor)
Spacer()
}
Spacer()
Expand Down
14 changes: 10 additions & 4 deletions Views/Library/ZimFilesCategories.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,12 @@ private struct CategoryGrid: View {
var body: some View {
Group {
if sections.isEmpty {
if viewModel.state == .inProgress {
switch viewModel.state {
case .inProgress:
Message(text: "zim_file_catalog.fetching.message".localized)
} else {
case .error:
Message(text: "library_refresh_error.retrieve.description".localized, color: .red)
case .initial, .complete:
Message(text: "zim_file_category.section.empty.message".localized)
}
} else {
Expand Down Expand Up @@ -224,9 +227,12 @@ private struct CategoryList: View {
var body: some View {
Group {
if zimFiles.isEmpty {
if viewModel.state == .inProgress {
switch viewModel.state {
case .inProgress:
Message(text: "zim_file_catalog.fetching.message".localized)
} else {
case .error:
Message(text: "library_refresh_error.retrieve.description".localized, color: .red)
case .initial, .complete:
Message(text: "zim_file_category.section.empty.message".localized)
}
} else {
Expand Down
7 changes: 5 additions & 2 deletions Views/Library/ZimFilesNew.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ struct ZimFilesNew: View {
}
.overlay {
if zimFiles.isEmpty {
if viewModel.state == .inProgress {
switch viewModel.state {
case .inProgress:
Message(text: "zim_file_catalog.fetching.message".localized)
} else {
case .error:
Message(text: "library_refresh_error.retrieve.description".localized, color: .red)
case .initial, .complete:
Message(text: "zim_file_new_overlay.empty".localized)
}
}
Expand Down
19 changes: 14 additions & 5 deletions Views/Settings/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,13 @@ struct LibrarySettings: View {
ProgressView().progressViewStyle(.circular).scaleEffect(0.5).frame(height: 1)
}
Spacer()
Text("library_settings.last_refresh.text".localized + ":").foregroundColor(.secondary)
LibraryLastRefreshTime().foregroundColor(.secondary)
if library.state == .error {
Text("library_refresh_error.retrieve.description".localized).foregroundColor(.red)
} else {
Text("library_settings.last_refresh.text".localized + ":").foregroundColor(.secondary)
LibraryLastRefreshTime().foregroundColor(.secondary)
}

}
VStack(alignment: .leading) {
Toggle("library_settings.auto_refresh.toggle".localized, isOn: $libraryAutoRefresh)
Expand Down Expand Up @@ -200,9 +205,13 @@ struct Settings: View {
var catalogSettings: some View {
Section {
HStack {
Text("catalog_settings.last_refresh.text".localized)
Spacer()
LibraryLastRefreshTime().foregroundColor(.secondary)
if library.state == .error {
Text("library_refresh_error.retrieve.description".localized).foregroundColor(.red)
} else {
Text("catalog_settings.last_refresh.text".localized)
Spacer()
LibraryLastRefreshTime().foregroundColor(.secondary)
}
}
if library.state == .inProgress {
HStack {
Expand Down
5 changes: 4 additions & 1 deletion Views/Welcome.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ struct Welcome: View {
logo
Divider()
actions
Text("library_refresh_error.retrieve.description".localized)
.foregroundColor(.red)
.opacity(library.state == .error ? 1 : 0)
Spacer()
}
#if os(macOS)
Expand All @@ -53,7 +56,7 @@ struct Welcome: View {
.padding()
.ignoresSafeArea()
.onChange(of: library.state) { state in
guard state != .inProgress else { return }
guard state == .complete else { return }
#if os(macOS)
navigation.currentItem = .categories
#elseif os(iOS)
Expand Down

0 comments on commit 58bfb76

Please sign in to comment.