Skip to content

Support multiple open items #829

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Zotero/Assets/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
"items.creator_summary.and" = "%@ and %@";
"items.creator_summary.etal" = "%@ et al.";
"items.retrieving_metadata" = "Retrieving Metadata";
"items.restore_open" = "Restore Open Items";

"lookup.title" = "Enter ISBNs, DOls, PMIDs, arXiv IDs, or ADS Bibcodes to add to your library:";

Expand Down Expand Up @@ -582,3 +583,10 @@
"accessibility.pdf.undo" = "Undo";
"accessibility.pdf.toggle_annotation_toolbar" = "Toggle annotation toolbar";
"accessibility.pdf.show_more_tools" = "Show more";
"accessibility.pdf.open_items" = "Open Items";
"accessibility.pdf.current_item" = "Current Item";
"accessibility.pdf.current_item_close" = "Close";
"accessibility.pdf.current_item_move_to_start" = "Move to start";
"accessibility.pdf.current_item_move_to end" = "Move to end";
"accessibility.pdf.close_all_open_items" = "Close all";
"accessibility.pdf.close_other_open_items" = "Close other items";
2 changes: 1 addition & 1 deletion Zotero/Controllers/Architecture/Coordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ enum SourceView {
protocol Coordinator: AnyObject {
var parentCoordinator: Coordinator? { get set }
var childCoordinators: [Coordinator] { get set }
var navigationController: UINavigationController? { get }
var navigationController: UINavigationController? { get set }

func start(animated: Bool)
func childDidFinish(_ child: Coordinator)
Expand Down
4 changes: 3 additions & 1 deletion Zotero/Controllers/Controllers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ final class UserControllers {
let webDavController: WebDavController
let customUrlController: CustomURLController
let fullSyncDebugger: FullSyncDebugger
let openItemsController: OpenItemsController
private let isFirstLaunch: Bool
private let lastBuildNumber: Int?
private unowned let translatorsAndStylesController: TranslatorsAndStylesController
Expand Down Expand Up @@ -406,7 +407,8 @@ final class UserControllers {
translatorsAndStylesController = controllers.translatorsAndStylesController
fullSyncDebugger = FullSyncDebugger(syncScheduler: syncScheduler, debugLogging: controllers.debugLogging, sessionController: controllers.sessionController)
idleTimerController = controllers.idleTimerController
customUrlController = CustomURLController(dbStorage: dbStorage, fileStorage: controllers.fileStorage)
openItemsController = OpenItemsController(dbStorage: dbStorage, fileStorage: controllers.fileStorage, attachmentDownloader: fileDownloader)
customUrlController = CustomURLController(dbStorage: dbStorage, openItemsController: openItemsController)
lastBuildNumber = controllers.lastBuildNumber
disposeBag = DisposeBag()
}
Expand Down
87 changes: 18 additions & 69 deletions Zotero/Controllers/CustomURLController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,32 @@ final class CustomURLController {

enum Kind {
case itemDetail(key: String, libraryId: LibraryIdentifier, preselectedChildKey: String?)
case itemReader(presentation: ItemPresentation, attachment: Attachment, isAvailable: Bool)
case itemReader(presentation: ItemPresentation)
}

private unowned let dbStorage: DbStorage
private unowned let fileStorage: FileStorage
private unowned let openItemsController: OpenItemsController

init(dbStorage: DbStorage, fileStorage: FileStorage) {
init(dbStorage: DbStorage, openItemsController: OpenItemsController) {
self.dbStorage = dbStorage
self.fileStorage = fileStorage
self.openItemsController = openItemsController
}

func process(url: URL) -> Kind? {
func process(url: URL, completion: @escaping(Kind?) -> Void) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
components.scheme == "zotero",
let action = components.host.flatMap({ CustomURLAction(rawValue: $0) })
else { return nil }
else {
completion(nil)
return
}

switch action {
case .select:
return select(path: components.path)
completion(select(path: components.path))

case .openItem:
return openItem(path: components.path, queryItems: components.queryItems ?? [])
openItem(path: components.path, queryItems: components.queryItems ?? [], completion: completion)
}

func select(path: String) -> Kind? {
Expand All @@ -59,67 +62,13 @@ final class CustomURLController {
}
}

func openItem(path: String, queryItems: [URLQueryItem]) -> Kind? {
guard let (key, libraryId, page, annotation) = extractProperties(from: path, and: queryItems, extractPageAndAnnotation: true, allowZotFileFormat: true) else { return nil }
return loadItemReaderKind(on: page, annotation: annotation, key: key, libraryId: libraryId)

func loadItemReaderKind(on page: Int?, annotation: String?, key: String, libraryId: LibraryIdentifier) -> Kind? {
var library: Library?
var item: RItem?
do {
try dbStorage.perform(on: .main) { coordinator in
library = try coordinator.perform(request: ReadLibraryDbRequest(libraryId: libraryId))
item = try coordinator.perform(request: ReadItemDbRequest(libraryId: libraryId, key: key))
}
} catch let error {
DDLogError("CustomURLConverter: library (\(libraryId)) or item (\(key)) not found - \(error)")
return nil
}
guard let library, let item else { return nil }
guard let attachment = AttachmentCreator.attachment(for: item, fileStorage: fileStorage, urlDetector: nil) else {
DDLogInfo("CustomURLConverter: trying to open incorrect item - \(item.rawType)")
return nil
}
guard case .file(let filename, let contentType, let location, _, _) = attachment.type else {
DDLogInfo("CustomURLConverter: trying to open invalid attachment type \(attachment.type)")
return nil
}
let parentKey = item.parent?.key
let file = Files.attachmentFile(in: libraryId, key: attachment.key, filename: filename, contentType: contentType)
let url = file.createUrl()
var presentation: ItemPresentation?
switch contentType {
case "application/pdf":
presentation = .pdf(library: library, key: key, parentKey: parentKey, url: url, page: page, preselectedAnnotationKey: annotation, previewRects: nil)

case "text/html":
if FeatureGates.enabled.contains(.htmlEpubReader) {
presentation = .html(library: library, key: key, parentKey: parentKey, url: url)
}

case "application/epub+zip":
if FeatureGates.enabled.contains(.htmlEpubReader) {
presentation = .epub(library: library, key: key, parentKey: parentKey, url: url)
}

default:
break
}
guard let presentation else {
DDLogInfo("CustomURLConverter: trying to open invalid content type \(contentType)")
return nil
}
switch location {
case .local:
return .itemReader(presentation: presentation, attachment: attachment, isAvailable: true)

case .remote, .localAndChangedRemotely:
return .itemReader(presentation: presentation, attachment: attachment, isAvailable: false)

case .remoteMissing:
DDLogInfo("CustomURLConverter: attachment \(attachment.key) missing remotely")
return nil
}
func openItem(path: String, queryItems: [URLQueryItem], completion: @escaping(Kind?) -> Void) {
guard let (key, libraryId, page, annotation) = extractProperties(from: path, and: queryItems, extractPageAndAnnotation: true, allowZotFileFormat: true) else {
completion(nil)
return
}
openItemsController.loadPresentation(for: key, libraryId: libraryId, page: page, preselectedAnnotationKey: annotation, previewRects: nil) { presentation in
completion(presentation.flatMap { Kind.itemReader(presentation: $0) })
}
}

Expand Down
12 changes: 12 additions & 0 deletions Zotero/Controllers/Database/Requests/ReadItemsDbRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,15 @@ struct ReadItemsWithKeysDbRequest: DbResponseRequest {
return database.objects(RItem.self).filter(.keys(keys, in: libraryId))
}
}

struct ReadItemsWithKeysFromMultipleLibrariesDbRequest: DbResponseRequest {
typealias Response = Results<RItem>

let keysByLibraryIdentifier: [LibraryIdentifier: Set<String>]

var needsWrite: Bool { return false }

func process(in database: Realm) throws -> Results<RItem> {
database.objects(RItem.self).filter(.keysByLibraryIdentifier(keysByLibraryIdentifier))
}
}
Loading