diff --git a/TestApp/Integrations/Carthage/project+lcp.yml b/TestApp/Integrations/Carthage/project+lcp.yml index dae39d83b..71d2ba30f 100644 --- a/TestApp/Integrations/Carthage/project+lcp.yml +++ b/TestApp/Integrations/Carthage/project+lcp.yml @@ -15,7 +15,7 @@ targets: TestApp: type: application platform: iOS - deploymentTarget: "14.0" + deploymentTarget: "18.0" sources: - path: Sources excludes: diff --git a/TestApp/Integrations/Carthage/project.yml b/TestApp/Integrations/Carthage/project.yml index bb9806e9d..a3a264099 100644 --- a/TestApp/Integrations/Carthage/project.yml +++ b/TestApp/Integrations/Carthage/project.yml @@ -15,7 +15,7 @@ targets: TestApp: type: application platform: iOS - deploymentTarget: "14.0" + deploymentTarget: "18.0" sources: - path: Sources excludes: diff --git a/TestApp/Integrations/CocoaPods/Podfile b/TestApp/Integrations/CocoaPods/Podfile index 718becbc5..1e97bca8b 100644 --- a/TestApp/Integrations/CocoaPods/Podfile +++ b/TestApp/Integrations/CocoaPods/Podfile @@ -1,4 +1,4 @@ -platform :ios, '14.0' +platform :ios, '18.0' target 'TestApp' do # Comment the next line if you don't want to use dynamic frameworks @@ -23,7 +23,7 @@ end post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '18.0' config.build_settings['ENABLE_BITCODE'] = 'NO' end end diff --git a/TestApp/Integrations/CocoaPods/Podfile+lcp b/TestApp/Integrations/CocoaPods/Podfile+lcp index 28124770d..07600cbb3 100644 --- a/TestApp/Integrations/CocoaPods/Podfile+lcp +++ b/TestApp/Integrations/CocoaPods/Podfile+lcp @@ -1,4 +1,4 @@ -platform :ios, '14.0' +platform :ios, '18.0' target 'TestApp' do # Comment the next line if you don't want to use dynamic frameworks @@ -26,7 +26,7 @@ end post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '18.0' config.build_settings['ENABLE_BITCODE'] = 'NO' end end diff --git a/TestApp/Integrations/CocoaPods/project+lcp.yml b/TestApp/Integrations/CocoaPods/project+lcp.yml index 741934ed2..e312a331c 100644 --- a/TestApp/Integrations/CocoaPods/project+lcp.yml +++ b/TestApp/Integrations/CocoaPods/project+lcp.yml @@ -5,7 +5,7 @@ targets: TestApp: type: application platform: iOS - deploymentTarget: "14.0" + deploymentTarget: "18.0" sources: - path: Sources excludes: diff --git a/TestApp/Integrations/CocoaPods/project.yml b/TestApp/Integrations/CocoaPods/project.yml index 578dd1ed9..76cf72d4e 100644 --- a/TestApp/Integrations/CocoaPods/project.yml +++ b/TestApp/Integrations/CocoaPods/project.yml @@ -5,7 +5,7 @@ targets: TestApp: type: application platform: iOS - deploymentTarget: "14.0" + deploymentTarget: "18.0" sources: - path: Sources excludes: diff --git a/TestApp/Integrations/Local/project+lcp.yml b/TestApp/Integrations/Local/project+lcp.yml index 908e289c3..4cc5bbbba 100644 --- a/TestApp/Integrations/Local/project+lcp.yml +++ b/TestApp/Integrations/Local/project+lcp.yml @@ -31,7 +31,7 @@ targets: TestApp: type: application platform: iOS - deploymentTarget: "14.0" + deploymentTarget: "18.0" sources: - path: Sources excludes: diff --git a/TestApp/Integrations/Local/project.yml b/TestApp/Integrations/Local/project.yml index fcd4aed6d..0c26b5d0d 100644 --- a/TestApp/Integrations/Local/project.yml +++ b/TestApp/Integrations/Local/project.yml @@ -29,7 +29,7 @@ targets: TestApp: type: application platform: iOS - deploymentTarget: "14.0" + deploymentTarget: "18.0" sources: - path: Sources excludes: diff --git a/TestApp/Integrations/SPM/project+lcp.yml b/TestApp/Integrations/SPM/project+lcp.yml index 56c561b9b..193da9226 100644 --- a/TestApp/Integrations/SPM/project+lcp.yml +++ b/TestApp/Integrations/SPM/project+lcp.yml @@ -23,7 +23,7 @@ targets: TestApp: type: application platform: iOS - deploymentTarget: "14.0" + deploymentTarget: "18.0" sources: - path: Sources excludes: diff --git a/TestApp/Integrations/SPM/project.yml b/TestApp/Integrations/SPM/project.yml index 3c1a2b7c4..af2477157 100644 --- a/TestApp/Integrations/SPM/project.yml +++ b/TestApp/Integrations/SPM/project.yml @@ -21,7 +21,7 @@ targets: TestApp: type: application platform: iOS - deploymentTarget: "14.0" + deploymentTarget: "18.0" sources: - path: Sources excludes: diff --git a/TestApp/Sources/Library/LibraryViewController.swift b/TestApp/Sources/Library/LibraryViewController.swift index 0e349329b..c91318b85 100644 --- a/TestApp/Sources/Library/LibraryViewController.swift +++ b/TestApp/Sources/Library/LibraryViewController.swift @@ -127,9 +127,7 @@ class LibraryViewController: UIViewController, Loggable { @objc func addBookFromDevice() { var types = DocumentTypes.main.supportedUTTypes - if let type = UTType(String(kUTTypeText)) { - types.append(type) - } + types.append(UTType.text) let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: types) documentPicker.delegate = self diff --git a/TestApp/Sources/OPDS/OPDSCatalogSelectorViewController.swift b/TestApp/Sources/OPDS/OPDSCatalogSelectorViewController.swift deleted file mode 100644 index 7211d73cc..000000000 --- a/TestApp/Sources/OPDS/OPDSCatalogSelectorViewController.swift +++ /dev/null @@ -1,172 +0,0 @@ -// -// Copyright 2024 Readium Foundation. All rights reserved. -// Use of this source code is governed by the BSD-style license -// available in the top-level LICENSE file of the project. -// - -import Foundation -import ReadiumOPDS -import ReadiumShared -import UIKit - -protocol OPDSCatalogSelectorViewControllerFactory { - func make() -> OPDSCatalogSelectorViewController -} - -class OPDSCatalogSelectorViewController: UITableViewController { - var catalogData: [[String: String]]? // An array of dicts in the form ["title": title, "url": url] - let cellReuseIdentifier = "catalogSelectorCell" - let userDefaultsID = "opdsCatalogArray" - var addFeedButton: UIBarButtonItem? - var mustEditAtIndexPath: IndexPath? - - override func viewDidLoad() { - preloadTestFeeds() - - tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier) - - tableView.frame = UIScreen.main.bounds - tableView.layoutMargins = UIEdgeInsets.zero - tableView.separatorInset = UIEdgeInsets.zero - tableView.cellLayoutMarginsFollowReadableWidth = false - tableView.sizeToFit() - - addFeedButton = UIBarButtonItem(title: NSLocalizedString("add_button", comment: "Add an OPDS feed button"), style: UIBarButtonItem.Style.plain, target: self, action: #selector(OPDSCatalogSelectorViewController.showAddFeedPopup)) - addFeedButton?.accessibilityLabel = NSLocalizedString("opds_add_button_a11y_label", comment: "Add an OPDS feed button") - - navigationItem.rightBarButtonItem = addFeedButton - } - - func preloadTestFeeds() { - let version = 2 - let VERSION_KEY = "OPDS_CATALOG_VERSION" - let OPDS2Catalog = ["title": "OPDS 2.0 Test Catalog", "url": "https://test.opds.io/2.0/home.json"] - let OTBCatalog = ["title": "Open Textbooks Catalog", "url": "http://open.minitex.org/textbooks"] - let SEBCatalog = ["title": "Standard eBooks Catalog", "url": "https://standardebooks.org/opds/all"] - - catalogData = UserDefaults.standard.array(forKey: userDefaultsID) as? [[String: String]] - let oldversion = UserDefaults.standard.integer(forKey: VERSION_KEY) - if catalogData == nil || oldversion < version { - UserDefaults.standard.set(version, forKey: VERSION_KEY) - catalogData = [ - OPDS2Catalog, OTBCatalog, SEBCatalog, - ] - UserDefaults.standard.set(catalogData, forKey: userDefaultsID) - } - } - - override func viewDidAppear(_ animated: Bool) { - if let index = mustEditAtIndexPath?.row { - showEditPopup(feedIndex: index) - } - mustEditAtIndexPath = nil - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - catalogData!.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) - cell.textLabel?.text = catalogData![indexPath.row]["title"] - return cell - } - - override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - tableView.layoutMargins = UIEdgeInsets.zero - tableView.separatorInset = UIEdgeInsets.zero - cell.layoutMargins = UIEdgeInsets.zero - cell.separatorInset = UIEdgeInsets.zero - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - self.tableView.deselectRow(at: indexPath, animated: false) - guard let urlString = catalogData![indexPath.row]["url"], - let url = URL(string: urlString) - else { - return - } - - let viewController: OPDSRootTableViewController = OPDSFactory.shared.make(feedURL: url, indexPath: indexPath) - navigationController?.pushViewController(viewController, animated: true) - } - - override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - // action one - let editAction = UIContextualAction( - style: .normal, - title: NSLocalizedString("edit_button", comment: "Edit a OPDS feed button") - ) { _, _, completion in - self.showEditPopup(feedIndex: indexPath.row) - completion(true) - } - editAction.backgroundColor = UIColor.gray - - // action two - let deleteAction = UIContextualAction( - style: .destructive, - title: NSLocalizedString("remove_button", comment: "Remove an OPDS feed button") - ) { _, _, completion in - self.catalogData?.remove(at: indexPath.row) - UserDefaults.standard.set(self.catalogData, forKey: self.userDefaultsID) - self.tableView.reloadData() - completion(true) - } - deleteAction.backgroundColor = UIColor.gray - - return UISwipeActionsConfiguration(actions: [editAction, deleteAction]) - } - - @objc func showAddFeedPopup() { - showEditPopup(feedIndex: nil) - } - - func showEditPopup(feedIndex: Int?, retry: Bool = false) { - let alertController = UIAlertController( - title: NSLocalizedString("opds_add_title", comment: "Title of the add feed alert"), - message: retry ? NSLocalizedString("opds_add_failure_message", comment: "Message when adding an invalid OPDS feed") : nil, - preferredStyle: .alert - ) - let confirmAction = UIAlertAction(title: NSLocalizedString("ok_button", comment: "Confirm addition of OPDS feed button"), style: .default) { _ in - if let title = alertController.textFields?[0].text, - let urlString = alertController.textFields?[1].text, - let url = URL(string: urlString) - { - OPDSParser.parseURL(url: url) { _, error in - DispatchQueue.main.async { - guard error == nil else { - self.showEditPopup(feedIndex: feedIndex, retry: true) - return - } - - if feedIndex == nil { - self.catalogData?.append(["title": title, "url": urlString]) - } else { - self.catalogData?[feedIndex!] = ["title": title, "url": urlString] - } - UserDefaults.standard.set(self.catalogData, forKey: self.userDefaultsID) - self.tableView.reloadData() - } - } - } else { - self.showEditPopup(feedIndex: feedIndex, retry: true) - } - } - let cancelAction = UIAlertAction(title: NSLocalizedString("cancel_button", comment: "Button to cancel addition of the OPDS feed"), style: .cancel) { _ in } - alertController.addTextField { textField in - textField.placeholder = NSLocalizedString("opds_feed_title_caption", comment: "Label for the OPDS feed title field") - if feedIndex != nil { - textField.text = self.catalogData![feedIndex!]["title"] - } - } - alertController.addTextField { textField in - textField.placeholder = NSLocalizedString("opds_feed_url_caption", comment: "Label for the OPDS feed URL field") - if feedIndex != nil { - textField.text = self.catalogData![feedIndex!]["url"] - } - } - alertController.addAction(confirmAction) - alertController.addAction(cancelAction) - present(alertController, animated: true, completion: nil) - } -} diff --git a/TestApp/Sources/OPDS/OPDSCatalogs/EditOPDSCatalogView.swift b/TestApp/Sources/OPDS/OPDSCatalogs/EditOPDSCatalogView.swift new file mode 100644 index 000000000..467741b38 --- /dev/null +++ b/TestApp/Sources/OPDS/OPDSCatalogs/EditOPDSCatalogView.swift @@ -0,0 +1,93 @@ +// +// Copyright 2024 Readium Foundation. All rights reserved. +// Use of this source code is governed by the BSD-style license +// available in the top-level LICENSE file of the project. +// + +import SwiftUI + +struct EditOPDSCatalogView: View { + @State var catalog: OPDSCatalog + var onSave: (OPDSCatalog) -> Void + + @Environment(\.presentationMode) var presentationMode + + @State private var showErrorAlert = false + @State private var errorTitle = "" + @State private var errorMessage = "" + @State private var urlString: String + + init( + catalog: OPDSCatalog, + onSave: @escaping (OPDSCatalog) -> Void + ) { + self.catalog = catalog + self.onSave = onSave + urlString = catalog.url.absoluteString + } + + var body: some View { + NavigationView { + Form { + Section(header: Text("opds_add_title")) { + TextField("Title", text: $catalog.title) + TextField("URL", text: $urlString) + .keyboardType(.URL) + .autocapitalization(.none) + .disableAutocorrection(true) + } + } + .navigationBarItems( + leading: Button("Cancel") { + presentationMode.wrappedValue.dismiss() + }, + trailing: Button("Save") { + validateAndSave() + } + ) + .alert(isPresented: $showErrorAlert) { + Alert( + title: Text(errorTitle), + message: Text(errorMessage), + dismissButton: .default(Text("OK")) + ) + } + } + } + + private func validateAndSave() { + let trimmedTitle = catalog.title.trimmingCharacters(in: .whitespacesAndNewlines) + + if trimmedTitle.isEmpty { + errorTitle = "Title Required" + errorMessage = "Please enter a title." + showErrorAlert = true + return + } + + if + let url = URL(string: urlString), + url.scheme != nil, + url.host != nil + { + catalog.url = url + onSave(catalog) + presentationMode.wrappedValue.dismiss() + } else { + errorTitle = "Invalid URL" + errorMessage = "Please enter a valid URL." + showErrorAlert = true + } + } +} + +#Preview { + EditOPDSCatalogView( + catalog: OPDSCatalog( + id: UUID().uuidString, + title: "OPDS 2.0 Test Catalog", + url: URL(string: "https://test.opds.io/2.0/home.json")! + ), + onSave: { _ in } + ) +} diff --git a/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalog.swift b/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalog.swift new file mode 100644 index 000000000..7ab7980de --- /dev/null +++ b/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalog.swift @@ -0,0 +1,35 @@ +// +// Copyright 2024 Readium Foundation. All rights reserved. +// Use of this source code is governed by the BSD-style license +// available in the top-level LICENSE file of the project. +// + +import Foundation + +struct OPDSCatalog: Identifiable, Equatable { + let id: String + var title: String + var url: URL + + var toDictionary: [String: String] { + [ + "id": id, + "title": title, + "url": url.absoluteString, + ] + } +} + +extension OPDSCatalog { + init?(dictionary: [String: String]) { + guard + let title = dictionary["title"], + let urlString = dictionary["url"], + let url = URL(string: urlString) + else { return nil } + + id = dictionary["id"] ?? UUID().uuidString + self.title = title + self.url = url + } +} diff --git a/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogRow.swift b/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogRow.swift new file mode 100644 index 000000000..c7cfff1d2 --- /dev/null +++ b/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogRow.swift @@ -0,0 +1,31 @@ +// +// Copyright 2024 Readium Foundation. All rights reserved. +// Use of this source code is governed by the BSD-style license +// available in the top-level LICENSE file of the project. +// + +import SwiftUI + +struct OPDSCatalogRow: View { + let title: String + + var body: some View { + HStack { + Image(systemName: "books.vertical.fill") + .foregroundColor(.accentColor) + Text(title) + + Spacer() + + Image(systemName: "chevron.right") + .foregroundColor(.gray) + } + } +} + +#Preview { + OPDSCatalogRow( + title: "OPDS 2.0 Test Catalog" + ) + .padding() +} diff --git a/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogsView.swift b/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogsView.swift new file mode 100644 index 000000000..31a3159e9 --- /dev/null +++ b/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogsView.swift @@ -0,0 +1,60 @@ +// +// Copyright 2024 Readium Foundation. All rights reserved. +// Use of this source code is governed by the BSD-style license +// available in the top-level LICENSE file of the project. +// + +import SwiftUI + +struct OPDSCatalogsView: View { + @StateObject private var viewModel: OPDSCatalogsViewModel + + init(viewModel: OPDSCatalogsViewModel) { + _viewModel = StateObject(wrappedValue: viewModel) + } + + var body: some View { + List(viewModel.catalogs) { catalog in + OPDSCatalogRow(title: catalog.title) + .contentShape(Rectangle()) + .onTapGesture { + viewModel.onCatalogTap(id: catalog.id) + } + .swipeActions(allowsFullSwipe: false) { + Button(role: .destructive) { + viewModel.onDeleteCatalogTap(id: catalog.id) + } label: { + Label("Delete", systemImage: "trash") + } + + Button { + viewModel.onEditCatalogTap(id: catalog.id) + } label: { + Label("Edit", systemImage: "pencil") + } + } + } + .listStyle(.plain) + .onAppear { + viewModel.viewDidAppear() + } + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + viewModel.onAddCatalogTap() + } label: { + Image(systemName: "plus") + } + } + } + .sheet(item: $viewModel.editingCatalog) { catalog in + EditOPDSCatalogView(catalog: catalog) { editingCatalog in + viewModel.onSaveEditedCatalogTap(editingCatalog) + } + } + } +} + +#Preview { + OPDSCatalogsView(viewModel: OPDSCatalogsViewModel()) +} diff --git a/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogsViewModel.swift b/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogsViewModel.swift new file mode 100644 index 000000000..4c0f02aaa --- /dev/null +++ b/TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogsViewModel.swift @@ -0,0 +1,125 @@ +// +// Copyright 2024 Readium Foundation. All rights reserved. +// Use of this source code is governed by the BSD-style license +// available in the top-level LICENSE file of the project. +// + +import Foundation + +final class OPDSCatalogsViewModel: ObservableObject { + @Published var catalogs: [OPDSCatalog] = [] { + didSet { + UserDefaults.standard.set( + catalogs.map(\.toDictionary), + forKey: userDefaultsID + ) + } + } + + @Published var editingCatalog: OPDSCatalog? + + var openCatalog: ((URL, IndexPath) -> Void)? + + private let userDefaultsID = "opdsCatalogArray" + private var isFirstAppear = false + + func viewDidAppear() { + guard !isFirstAppear else { return } + isFirstAppear = true + preloadTestFeeds() + } + + func onCatalogTap(id: OPDSCatalog.ID) { + guard + let openCatalog, + let index = catalogs.firstIndex(where: { $0.id == id }) + else { + assertionFailure("openCatalog closure have to be set") + return + } + openCatalog(catalogs[index].url, IndexPath(row: index, section: 0)) + } + + func onEditCatalogTap(id: OPDSCatalog.ID) { + guard + let catalog = catalogs.first(where: { $0.id == id }) + else { return } + editingCatalog = catalog + } + + func onDeleteCatalogTap(id: OPDSCatalog.ID) { + guard + let index = catalogs.firstIndex(where: { $0.id == id }) + else { return } + catalogs.remove(at: index) + } + + func onSaveEditedCatalogTap(_ catalog: OPDSCatalog) { + if + let index = catalogs.firstIndex(where: { $0.id == catalog.id }) + { + catalogs[index] = catalog + } else { + catalogs.append(catalog) + } + editingCatalog = nil + } + + func onAddCatalogTap() { + let newCatalog = OPDSCatalog( + id: UUID().uuidString, + title: "", + url: URL(string: "http://")! + ) + editingCatalog = newCatalog + } + + private func preloadTestFeeds() { + let catalogsArray = UserDefaults.standard.array(forKey: userDefaultsID) as? [[String: String]] + catalogs = catalogsArray? + .compactMap(OPDSCatalog.init) ?? [] + + let oldVersion = UserDefaults.standard.integer(forKey: .versionKey) + + if + catalogs.isEmpty || oldVersion < .currentVersion + { + setDefaultCatalogs() + } + } + + private func setDefaultCatalogs() { + UserDefaults.standard.set(.currentVersion, forKey: .versionKey) + catalogs = .testData + } +} + +private extension String { + static let versionKey = "VERSION_KEY" +} + +private extension Int { + static let currentVersion = 2 +} + +private extension [[String: String]] { + static let testData: [[String: String]] = [ + ["title": "OPDS 2.0 Test Catalog", "url": "https://test.opds.io/2.0/home.json"], + ["title": "Open Textbooks Catalog", "url": "http://open.minitex.org/textbooks"], + ] +} + +private extension Array where Element == OPDSCatalog { + static let testData: [OPDSCatalog] = [ + OPDSCatalog( + id: UUID().uuidString, + title: "OPDS 2.0 Test Catalog", + url: URL(string: "https://test.opds.io/2.0/home.json")! + ), + OPDSCatalog( + id: UUID().uuidString, + title: "Open Textbooks Catalog", + url: URL(string: "http://open.minitex.org/textbooks")! + ), + ] +} diff --git a/TestApp/Sources/OPDS/OPDSFactory.swift b/TestApp/Sources/OPDS/OPDSFactory.swift index d4a5f298e..21dbd86b0 100644 --- a/TestApp/Sources/OPDS/OPDSFactory.swift +++ b/TestApp/Sources/OPDS/OPDSFactory.swift @@ -6,6 +6,7 @@ import Foundation import ReadiumShared +import SwiftUI import UIKit final class OPDSFactory { @@ -16,13 +17,6 @@ final class OPDSFactory { private let storyboard = UIStoryboard(name: "OPDS", bundle: nil) } -extension OPDSFactory: OPDSCatalogSelectorViewControllerFactory { - func make() -> OPDSCatalogSelectorViewController { - let controller = storyboard.instantiateViewController(withIdentifier: "OPDSCatalogSelectorViewController") as! OPDSCatalogSelectorViewController - return controller - } -} - extension OPDSFactory: OPDSRootTableViewControllerFactory { func make(feedURL: URL, indexPath: IndexPath?) -> OPDSRootTableViewController { let controller = storyboard.instantiateViewController(withIdentifier: "OPDSRootTableViewController") as! OPDSRootTableViewController diff --git a/TestApp/Sources/OPDS/OPDSModule.swift b/TestApp/Sources/OPDS/OPDSModule.swift index 2477a974c..02f07e4ac 100644 --- a/TestApp/Sources/OPDS/OPDSModule.swift +++ b/TestApp/Sources/OPDS/OPDSModule.swift @@ -7,6 +7,7 @@ import Combine import Foundation import ReadiumShared +import SwiftUI import UIKit enum OPDSError: Error { @@ -24,7 +25,7 @@ protocol OPDSModuleAPI { protocol OPDSModuleDelegate: ModuleDelegate { /// Called when an OPDS publication needs to be downloaded. - func opdsDownloadPublication(_ publication: Publication?, at link: Link, sender: UIViewController) async throws -> Book + func opdsDownloadPublication(_ publication: Publication?, at link: ReadiumShared.Link, sender: UIViewController) async throws -> Book } final class OPDSModule: OPDSModuleAPI { @@ -38,7 +39,24 @@ final class OPDSModule: OPDSModuleAPI { } private(set) lazy var rootViewController: UINavigationController = { - let catalogViewController: OPDSCatalogSelectorViewController = factory.make() - return UINavigationController(rootViewController: catalogViewController) + let viewModel = OPDSCatalogsViewModel() + + let catalogViewController = UIHostingController( + rootView: OPDSCatalogsView(viewModel: viewModel) + ) + + let navigationController = UINavigationController( + rootViewController: catalogViewController + ) + + viewModel.openCatalog = { [weak navigationController] url, indexPath in + let viewController = OPDSFactory.shared.make( + feedURL: url, + indexPath: indexPath + ) + navigationController?.pushViewController(viewController, animated: true) + } + + return navigationController }() } diff --git a/TestApp/Sources/OPDS/OPDSRootTableViewController.swift b/TestApp/Sources/OPDS/OPDSRootTableViewController.swift index 027da8027..8db81463d 100644 --- a/TestApp/Sources/OPDS/OPDSRootTableViewController.swift +++ b/TestApp/Sources/OPDS/OPDSRootTableViewController.swift @@ -586,12 +586,6 @@ extension OPDSRootTableViewController: OPDSFacetViewControllerDelegate { // MARK: - UINavigationController delegate and tooling extension OPDSRootTableViewController: UINavigationControllerDelegate { - func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { - if mustEditFeed { - (viewController as? OPDSCatalogSelectorViewController)?.mustEditAtIndexPath = originalFeedIndexPath - } - } - fileprivate func pushOpdsRootViewController(href: String) { guard let url = URL(string: href) else { return