mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-08-03 04:27:00 -04:00
Merge pull request #1258 from kiwix/1255-default-select-opened-file
Open ZimFileDetails when importing a single file
This commit is contained in:
commit
62e9a33308
@ -178,8 +178,13 @@ final class SplitViewController: UISplitViewController {
|
|||||||
}()
|
}()
|
||||||
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
||||||
case .opened:
|
case .opened:
|
||||||
let controller = UIHostingController(rootView: ZimFilesOpened(dismiss: nil))
|
// workaround for programatic triggering ZimFileDetails
|
||||||
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
// on iPad full screen view
|
||||||
|
let navHelper = NavigationHelper()
|
||||||
|
let controller = UIHostingController(rootView: ZimFilesOpened(navigationHelper: navHelper))
|
||||||
|
let navController = UINavigationController(rootViewController: controller)
|
||||||
|
navHelper.navigationController = navController
|
||||||
|
setViewController(navController, for: .secondary)
|
||||||
case .categories:
|
case .categories:
|
||||||
let controller = UIHostingController(rootView: ZimFilesCategories(dismiss: nil))
|
let controller = UIHostingController(rootView: ZimFilesCategories(dismiss: nil))
|
||||||
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
||||||
|
@ -71,6 +71,7 @@ extension Notification.Name {
|
|||||||
static let alert = Notification.Name("alert")
|
static let alert = Notification.Name("alert")
|
||||||
static let openFiles = Notification.Name("openFiles")
|
static let openFiles = Notification.Name("openFiles")
|
||||||
static let openURL = Notification.Name("openURL")
|
static let openURL = Notification.Name("openURL")
|
||||||
|
static let selectFile = Notification.Name("selectFile")
|
||||||
static let exportFileData = Notification.Name("exportFileData")
|
static let exportFileData = Notification.Name("exportFileData")
|
||||||
static let saveContent = Notification.Name("saveContent")
|
static let saveContent = Notification.Name("saveContent")
|
||||||
static let toggleSidebar = Notification.Name("toggleSidebar")
|
static let toggleSidebar = Notification.Name("toggleSidebar")
|
||||||
@ -111,6 +112,11 @@ extension NotificationCenter {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
static func selectFileBy(fileId: UUID) {
|
||||||
|
NotificationCenter.default.post(name: .selectFile, object: nil, userInfo: ["fileId": fileId])
|
||||||
|
}
|
||||||
|
|
||||||
static func openFiles(_ urls: [URL], context: OpenFileContext) {
|
static func openFiles(_ urls: [URL], context: OpenFileContext) {
|
||||||
NotificationCenter.default.post(name: .openFiles, object: nil, userInfo: ["urls": urls, "context": context])
|
NotificationCenter.default.post(name: .openFiles, object: nil, userInfo: ["urls": urls, "context": context])
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ struct Library: View {
|
|||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
.navigationTitle(MenuItem.categories.name)
|
.navigationTitle(MenuItem.categories.name)
|
||||||
case .opened:
|
case .opened:
|
||||||
ZimFilesOpened(dismiss: dismiss)
|
ZimFilesOpenedNavStack(dismiss: dismiss)
|
||||||
case .downloads:
|
case .downloads:
|
||||||
ZimFilesDownloads(dismiss: dismiss)
|
ZimFilesDownloads(dismiss: dismiss)
|
||||||
.environment(\.managedObjectContext, Database.shared.viewContext)
|
.environment(\.managedObjectContext, Database.shared.viewContext)
|
||||||
|
@ -27,6 +27,8 @@ struct ZimFilesMultiOpened: View {
|
|||||||
) private var zimFiles: FetchedResults<ZimFile>
|
) private var zimFiles: FetchedResults<ZimFile>
|
||||||
@State private var isFileImporterPresented = false
|
@State private var isFileImporterPresented = false
|
||||||
@StateObject private var selection = MultiSelectedZimFilesViewModel()
|
@StateObject private var selection = MultiSelectedZimFilesViewModel()
|
||||||
|
private let selectFileById = NotificationCenter.default.publisher(for: .selectFile)
|
||||||
|
@State private var fileIdToOpen: UUID?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@ -57,9 +59,23 @@ struct ZimFilesMultiOpened: View {
|
|||||||
Message(text: LocalString.zim_file_opened_overlay_no_opened_message)
|
Message(text: LocalString.zim_file_opened_overlay_no_opened_message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onReceive(selectFileById, perform: { notification in
|
||||||
|
guard let fileId = notification.userInfo?["fileId"] as? UUID else {
|
||||||
|
fileIdToOpen = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileIdToOpen = fileId
|
||||||
|
})
|
||||||
.onChange(of: zimFiles.count) { _ in
|
.onChange(of: zimFiles.count) { _ in
|
||||||
if let firstZimFile = zimFiles.first {
|
let selectedZimFile: ZimFile?
|
||||||
selection.singleSelect(zimFile: firstZimFile)
|
if let fileIdToOpen {
|
||||||
|
selectedZimFile = zimFiles.first { $0.fileID == fileIdToOpen }
|
||||||
|
self.fileIdToOpen = nil
|
||||||
|
} else {
|
||||||
|
selectedZimFile = zimFiles.first
|
||||||
|
}
|
||||||
|
if let selectedZimFile {
|
||||||
|
selection.singleSelect(zimFile: selectedZimFile)
|
||||||
} else {
|
} else {
|
||||||
selection.reset()
|
selection.reset()
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,21 @@ import SwiftUI
|
|||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
|
||||||
|
final class NavigationHelper {
|
||||||
|
weak var navigationController: UINavigationController?
|
||||||
|
func push<V: View>(@ViewBuilder _ view: () -> V) {
|
||||||
|
let hostingVC = UIHostingController(rootView: view())
|
||||||
|
navigationController?.pushViewController(hostingVC, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A grid of zim files that are opened, or was open but is now missing
|
/// A grid of zim files that are opened, or was open but is now missing
|
||||||
/// iOS only
|
/// iOS only, only iPad splitView
|
||||||
|
/// the UINavigationController used in splitView doesn't work with
|
||||||
|
/// NavigationStack
|
||||||
|
/// therefore programatic selection of newly added file is with a
|
||||||
|
/// workaround
|
||||||
struct ZimFilesOpened: View {
|
struct ZimFilesOpened: View {
|
||||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
@FetchRequest(
|
@FetchRequest(
|
||||||
@ -28,7 +41,9 @@ struct ZimFilesOpened: View {
|
|||||||
) private var zimFiles: FetchedResults<ZimFile>
|
) private var zimFiles: FetchedResults<ZimFile>
|
||||||
@State private var isFileImporterPresented = false
|
@State private var isFileImporterPresented = false
|
||||||
@EnvironmentObject var selection: SelectedZimFileViewModel
|
@EnvironmentObject var selection: SelectedZimFileViewModel
|
||||||
let dismiss: (() -> Void)? // iOS only
|
let navigationHelper: NavigationHelper
|
||||||
|
private let selectFileById = NotificationCenter.default.publisher(for: .selectFile)
|
||||||
|
@State private var fileIdToOpen: UUID?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
LazyVGrid(
|
LazyVGrid(
|
||||||
@ -37,17 +52,15 @@ struct ZimFilesOpened: View {
|
|||||||
spacing: 12
|
spacing: 12
|
||||||
) {
|
) {
|
||||||
ForEach(zimFiles) { zimFile in
|
ForEach(zimFiles) { zimFile in
|
||||||
LibraryZimFileContext(
|
NavigationLink {
|
||||||
content: {
|
ZimFileDetail(zimFile: zimFile, dismissParent: nil)
|
||||||
ZimFileCell(
|
} label: {
|
||||||
zimFile,
|
ZimFileCell(
|
||||||
prominent: .name,
|
zimFile,
|
||||||
isSelected: selection.isSelected(zimFile)
|
prominent: .name,
|
||||||
)
|
isSelected: selection.isSelected(zimFile)
|
||||||
},
|
)
|
||||||
zimFile: zimFile,
|
} .accessibilityIdentifier(zimFile.name)
|
||||||
selection: selection,
|
|
||||||
dismiss: dismiss)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modifier(GridCommon(edges: .all))
|
.modifier(GridCommon(edges: .all))
|
||||||
@ -58,13 +71,34 @@ struct ZimFilesOpened: View {
|
|||||||
Message(text: LocalString.zim_file_opened_overlay_no_opened_message)
|
Message(text: LocalString.zim_file_opened_overlay_no_opened_message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onReceive(selectFileById, perform: { notification in
|
||||||
|
guard let fileId = notification.userInfo?["fileId"] as? UUID else {
|
||||||
|
fileIdToOpen = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileIdToOpen = fileId
|
||||||
|
})
|
||||||
.onChange(of: zimFiles.count) { _ in
|
.onChange(of: zimFiles.count) { _ in
|
||||||
if let firstZimFile = zimFiles.first {
|
let selectedZimFile: ZimFile?
|
||||||
selection.selectedZimFile = firstZimFile
|
if let fileIdToOpen {
|
||||||
|
selectedZimFile = zimFiles.first { $0.fileID == fileIdToOpen }
|
||||||
|
self.fileIdToOpen = nil
|
||||||
|
} else {
|
||||||
|
selectedZimFile = nil
|
||||||
|
}
|
||||||
|
if let selectedZimFile {
|
||||||
|
selection.selectedZimFile = selectedZimFile
|
||||||
} else {
|
} else {
|
||||||
selection.reset()
|
selection.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onReceive(selection.$selectedZimFile, perform: { selectedZimFile in
|
||||||
|
if let selectedZimFile {
|
||||||
|
navigationHelper.push {
|
||||||
|
ZimFileDetail(zimFile: selectedZimFile, dismissParent: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
// not using OpenFileButton here, because it does not work on iOS/iPadOS 15 when this view is in a modal
|
// not using OpenFileButton here, because it does not work on iOS/iPadOS 15 when this view is in a modal
|
||||||
.fileImporter(
|
.fileImporter(
|
||||||
isPresented: $isFileImporterPresented,
|
isPresented: $isFileImporterPresented,
|
||||||
|
101
Views/Library/ZimFilesOpenedNavStack.swift
Normal file
101
Views/Library/ZimFilesOpenedNavStack.swift
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// This file is part of Kiwix for iOS & macOS.
|
||||||
|
//
|
||||||
|
// Kiwix is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// Kiwix is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
/// A grid of zim files that are opened, or was open but is now missing
|
||||||
|
/// iOS only
|
||||||
|
struct ZimFilesOpenedNavStack: View {
|
||||||
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
|
@FetchRequest(
|
||||||
|
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
|
||||||
|
predicate: ZimFile.Predicate.isDownloaded,
|
||||||
|
animation: .easeInOut
|
||||||
|
) private var zimFiles: FetchedResults<ZimFile>
|
||||||
|
@State private var isFileImporterPresented = false
|
||||||
|
@State private var navPath: [ZimFile] = []
|
||||||
|
// opening the details of a freshly added zimFile
|
||||||
|
private let selectFileById = NotificationCenter.default.publisher(for: .selectFile)
|
||||||
|
@State private var fileIdToOpen: UUID?
|
||||||
|
|
||||||
|
let dismiss: (() -> Void)?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack(path: $navPath) {
|
||||||
|
LazyVGrid(
|
||||||
|
columns: ([GridItem(.adaptive(minimum: 250, maximum: 500), spacing: 12)]),
|
||||||
|
alignment: .leading,
|
||||||
|
spacing: 12
|
||||||
|
) {
|
||||||
|
ForEach(zimFiles) { zimFile in
|
||||||
|
NavigationLink(value: zimFile) {
|
||||||
|
ZimFileCell(
|
||||||
|
zimFile,
|
||||||
|
prominent: .name,
|
||||||
|
isSelected: navPath.contains(where: { $0.fileID == zimFile.fileID })
|
||||||
|
)
|
||||||
|
}.accessibilityIdentifier(zimFile.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationDestination(for: ZimFile.self) { zimFile in
|
||||||
|
ZimFileDetail(zimFile: zimFile, dismissParent: dismiss)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modifier(GridCommon(edges: .all))
|
||||||
|
.modifier(ToolbarRoleBrowser())
|
||||||
|
.navigationTitle(MenuItem.opened.name)
|
||||||
|
.overlay {
|
||||||
|
if zimFiles.isEmpty {
|
||||||
|
Message(text: LocalString.zim_file_opened_overlay_no_opened_message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
|
Button {
|
||||||
|
isFileImporterPresented = true
|
||||||
|
} label: {
|
||||||
|
Label(LocalString.zim_file_opened_toolbar_open_title, systemImage: "plus")
|
||||||
|
}.help(LocalString.zim_file_opened_toolbar_open_help)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// not using OpenFileButton here, because it does not work on iOS/iPadOS 15 when this view is in a modal
|
||||||
|
.fileImporter(
|
||||||
|
isPresented: $isFileImporterPresented,
|
||||||
|
allowedContentTypes: [UTType.zimFile],
|
||||||
|
allowsMultipleSelection: true
|
||||||
|
) { result in
|
||||||
|
guard case let .success(urls) = result else { return }
|
||||||
|
NotificationCenter.openFiles(urls, context: .library)
|
||||||
|
}
|
||||||
|
.onReceive(selectFileById, perform: { notification in
|
||||||
|
guard let fileId = notification.userInfo?["fileId"] as? UUID else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileIdToOpen = fileId
|
||||||
|
})
|
||||||
|
.onChange(of: zimFiles.count) { _ in
|
||||||
|
if let fileIdToOpen,
|
||||||
|
let selectedZimFile = zimFiles.first(where: { $0.fileID == fileIdToOpen }) {
|
||||||
|
self.fileIdToOpen = nil
|
||||||
|
navPath = [selectedZimFile]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -81,6 +81,11 @@ struct OpenFileHandler: ViewModifier {
|
|||||||
// action for zim files that can be opened (e.g. open main page)
|
// action for zim files that can be opened (e.g. open main page)
|
||||||
if case .library = context {
|
if case .library = context {
|
||||||
// don't need to open the main page
|
// don't need to open the main page
|
||||||
|
// but we should select it to show the details
|
||||||
|
// if there's only one ZIM file imported
|
||||||
|
if openedZimFileIDs.count == 1, let firstFileID = openedZimFileIDs.first {
|
||||||
|
NotificationCenter.selectFileBy(fileId: firstFileID)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for fileID in openedZimFileIDs {
|
for fileID in openedZimFileIDs {
|
||||||
if let url = await ZimFileService.shared.getMainPageURL(zimFileID: fileID) {
|
if let url = await ZimFileService.shared.getMainPageURL(zimFileID: fileID) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user