From b2208616b6fed0da33c2bbd03557dd2cfd3a5c0f Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Thu, 30 Nov 2023 16:58:12 +0200 Subject: [PATCH] Add support for different open items per session --- Zotero/Controllers/OpenItemsController.swift | 104 ++++++++++-------- Zotero/Scenes/AppCoordinator.swift | 14 +-- Zotero/Scenes/Detail/DetailCoordinator.swift | 23 +++- .../Items/Views/ItemsViewController.swift | 8 +- .../PDF/Views/PDFReaderViewController.swift | 20 ++-- .../General/NoteEditorCoordinator.swift | 5 +- .../Views/NoteEditorViewController.swift | 32 ++++-- .../Main/Views/MainViewController.swift | 7 +- 8 files changed, 132 insertions(+), 81 deletions(-) diff --git a/Zotero/Controllers/OpenItemsController.swift b/Zotero/Controllers/OpenItemsController.swift index c28ea8526..42c60d2ff 100644 --- a/Zotero/Controllers/OpenItemsController.swift +++ b/Zotero/Controllers/OpenItemsController.swift @@ -96,40 +96,44 @@ final class OpenItemsController { private unowned let dbStorage: DbStorage private unowned let fileStorage: FileStorage // TODO: Use a better data structure, such as an ordered set - private(set) var items: [Item] = [] - private var itemsToken: NotificationToken? - public var itemsSortedByUserOrder: [Item] { - items.sorted(by: { $0.userIndex < $1.userIndex }) - } - public var itemsSortedByLastOpen: [Item] { - items.sorted(by: { $0.lastOpened > $1.lastOpened }) - } - let observable: PublishSubject<[Item]> + private var itemsBySessionIdentifier: [String: [Item]] = [:] + private var itemsTokenBySessionIdentifier: [String: NotificationToken] = [:] + private var observableBySessionIdentifier: [String: PublishSubject<[Item]>] = [:] private let disposeBag: DisposeBag // MARK: Object Lifecycle init(dbStorage: DbStorage, fileStorage: FileStorage) { self.dbStorage = dbStorage self.fileStorage = fileStorage - observable = PublishSubject() disposeBag = DisposeBag() } // MARK: Actions - func setItems(_ items: [Item], validate: Bool) { - DDLogInfo("OpenItemsController: setting items \(items)") - var finalItems = items - if validate { - finalItems = filterValidItems(items) + func observable(for sessionIdentifier: String) -> PublishSubject<[Item]> { + if let observable = observableBySessionIdentifier[sessionIdentifier] { + return observable } - guard finalItems != self.items else { return } + let observable = PublishSubject<[Item]>() + observableBySessionIdentifier[sessionIdentifier] = observable + return observable + } + + func getItems(for sessionIdentifier: String) -> [Item] { + itemsBySessionIdentifier[sessionIdentifier, default: []] + } + + func setItems(_ items: [Item], for sessionIdentifier: String, validate: Bool) { + DDLogInfo("OpenItemsController: setting items \(items) for \(sessionIdentifier)") + let existingItems = getItems(for: sessionIdentifier) + let newItems = validate ? filterValidItems(items) : items + guard newItems != existingItems else { return } // Invalidate previous observer first. - itemsToken?.invalidate() - itemsToken = nil - self.items = finalItems + itemsTokenBySessionIdentifier[sessionIdentifier]?.invalidate() + itemsTokenBySessionIdentifier[sessionIdentifier] = nil + itemsBySessionIdentifier[sessionIdentifier] = newItems // Register observer for newly set items. - itemsToken = registerObserver(for: finalItems) - observable.on(.next(finalItems)) + itemsTokenBySessionIdentifier[sessionIdentifier] = registerObserver(for: newItems) + observable(for: sessionIdentifier).on(.next(newItems)) func registerObserver(for items: [Item]) -> NotificationToken? { var token: NotificationToken? @@ -152,7 +156,8 @@ final class OpenItemsController { case .update(_, let deletions, _, _): if !deletions.isEmpty, let self { // Observed items have been deleted, call setItems to validate and register new observer. - self.setItems(self.items, validate: true) + let existingItems = getItems(for: sessionIdentifier) + setItems(existingItems, for: sessionIdentifier, validate: true) } case .error(let error): @@ -161,24 +166,26 @@ final class OpenItemsController { } } } catch let error { - DDLogError("OpenItemsController: can't setup items observer - \(error)") + DDLogError("OpenItemsController: can't register items observer - \(error)") } return token } } - func open(_ kind: Item.Kind) { - DDLogInfo("OpenItemsController: opened item \(kind)") - if let index = items.firstIndex(where: { $0.kind == kind }) { - items[index].lastOpened = .now - DDLogInfo("OpenItemsController: already opened item \(kind) became most recent") - observable.on(.next(items)) + func open(_ kind: Item.Kind, for sessionIdentifier: String) { + DDLogInfo("OpenItemsController: opened item \(kind) for \(sessionIdentifier)") + var existingItems = getItems(for: sessionIdentifier) + if let index = existingItems.firstIndex(where: { $0.kind == kind }) { + existingItems[index].lastOpened = .now + itemsBySessionIdentifier[sessionIdentifier] = existingItems + DDLogInfo("OpenItemsController: already opened item \(kind) became most recent for \(sessionIdentifier)") + observable(for: sessionIdentifier).on(.next(existingItems)) } else { - DDLogInfo("OpenItemsController: newly opened item \(kind) set as most recent") - let item = Item(kind: kind, userIndex: items.count) - let newItems = items + [item] + DDLogInfo("OpenItemsController: newly opened item \(kind) set as most recent for \(sessionIdentifier)") + let item = Item(kind: kind, userIndex: existingItems.count) + let newItems = existingItems + [item] // setItems will produce next observable event - setItems(newItems, validate: false) + setItems(newItems, for: sessionIdentifier, validate: false) } } @@ -190,26 +197,27 @@ final class OpenItemsController { } @discardableResult - func restoreMostRecentlyOpenedItem(using presenter: OpenItemsPresenter) -> Item? { + func restoreMostRecentlyOpenedItem(using presenter: OpenItemsPresenter, sessionIdentifier: String) -> Item? { // Will restore most recent opened item still present, or none if all fail - DDLogInfo("OpenItemsController: restoring most recently opened item using presenter \(presenter)") + var existingItems = getItems(for: sessionIdentifier) + DDLogInfo("OpenItemsController: restoring most recently opened item using presenter \(presenter) for \(sessionIdentifier)") var itemsChanged: Bool = false defer { if itemsChanged { - observable.on(.next(items)) + observable(for: sessionIdentifier).on(.next(existingItems)) } } var item: Item? var presentation: Presentation? - let itemsSortedByLastOpen = itemsSortedByLastOpen - for _item in itemsSortedByLastOpen { + let existingItemsSortedByLastOpen = itemsSortedByLastOpen(for: sessionIdentifier) + for _item in existingItemsSortedByLastOpen { if let _presentation = loadPresentation(for: _item) { item = _item presentation = _presentation break } - DDLogWarn("OpenItemsController: removing not loaded item \(_item)") - items.removeAll(where: { $0 == _item }) + DDLogWarn("OpenItemsController: removing not loaded item \(_item) for \(sessionIdentifier)") + existingItems.removeAll(where: { $0 == _item }) itemsChanged = true } guard let item, let presentation else { return nil } @@ -217,16 +225,16 @@ final class OpenItemsController { return item } - func deferredOpenItemsMenuElement(disableOpenItem: Bool, itemActionCallback: @escaping (Item, UIAction) -> Void) -> UIDeferredMenuElement { + func deferredOpenItemsMenuElement(for sessionIdentifier: String, disableOpenItem: Bool, itemActionCallback: @escaping (Item, UIAction) -> Void) -> UIDeferredMenuElement { UIDeferredMenuElement { [weak self] elementProvider in guard let self else { elementProvider([]) return } var actions: [UIAction] = [] - let openItem: Item? = disableOpenItem ? itemsSortedByLastOpen.first : nil - let itemsSortedByUserOrder = itemsSortedByUserOrder - var itemTuples: [(Item, RItem)] = filterValidItemsWithRItem(itemsSortedByUserOrder) + let openItem: Item? = disableOpenItem ? itemsSortedByLastOpen(for: sessionIdentifier).first : nil + let existingItemsSortedByLastOpen = itemsSortedByUserOrder(for: sessionIdentifier) + var itemTuples: [(Item, RItem)] = filterValidItemsWithRItem(existingItemsSortedByLastOpen) for (item, rItem) in itemTuples { var attributes: UIMenuElement.Attributes = [] var state: UIMenuElement.State = .off @@ -244,6 +252,14 @@ final class OpenItemsController { } // MARK: Helper Methods + private func itemsSortedByUserOrder(for sessionIdentifier: String) -> [Item] { + getItems(for: sessionIdentifier).sorted(by: { $0.userIndex < $1.userIndex }) + } + + private func itemsSortedByLastOpen(for sessionIdentifier: String) -> [Item] { + getItems(for: sessionIdentifier).sorted(by: { $0.lastOpened > $1.lastOpened }) + } + private func filterValidItemsWithRItem(_ items: [Item]) -> [(Item, RItem)] { var itemTuples: [(Item, RItem)] = [] do { diff --git a/Zotero/Scenes/AppCoordinator.swift b/Zotero/Scenes/AppCoordinator.swift index 6de3d10b5..d059001ca 100644 --- a/Zotero/Scenes/AppCoordinator.swift +++ b/Zotero/Scenes/AppCoordinator.swift @@ -126,8 +126,8 @@ extension AppCoordinator: AppDelegateCoordinatorDelegate { conflictAlertQueueController = nil controllers.userControllers?.syncScheduler.syncController.set(coordinator: nil) } else { - let controller = MainViewController(controllers: controllers) (urlContext, data) = preprocess(connectionOptions: options, session: session) + let controller = MainViewController(sessionIdentifier: session.persistentIdentifier, controllers: controllers) viewController = controller conflictReceiverAlertController = ConflictReceiverAlertController(viewController: controller) @@ -137,7 +137,7 @@ extension AppCoordinator: AppDelegateCoordinatorDelegate { DDLogInfo("AppCoordinator: show main screen logged \(isLoggedIn ? "in" : "out"); animated=\(animated)") show(viewController: viewController, in: window, animated: animated) { - process(urlContext: urlContext, data: data) + process(urlContext: urlContext, data: data, sessionIdentifier: session.persistentIdentifier) } func show(viewController: UIViewController?, in window: UIWindow, animated: Bool = false, completion: @escaping () -> Void) { @@ -160,12 +160,12 @@ extension AppCoordinator: AppDelegateCoordinatorDelegate { DDLogInfo("AppCoordinator: Preprocessing restored state - \(data)") Defaults.shared.selectedLibrary = data.libraryId Defaults.shared.selectedCollectionId = data.collectionId - controllers.userControllers?.openItemsController.setItems(data.openItems, validate: true) + controllers.userControllers?.openItemsController.setItems(data.openItems, for: session.persistentIdentifier, validate: true) } return (urlContext, data) } - func process(urlContext: UIOpenURLContext?, data: RestoredStateData?) { + func process(urlContext: UIOpenURLContext?, data: RestoredStateData?, sessionIdentifier: String) { if let urlContext, let urlController = controllers.userControllers?.customUrlController { // If scene was started from custom URL let sourceApp = urlContext.options.sourceApplication ?? "unknown" @@ -180,10 +180,10 @@ extension AppCoordinator: AppDelegateCoordinatorDelegate { if let data, data.restoreMostRecentlyOpenedItem { DDLogInfo("AppCoordinator: Processing restored state - \(data)") // If scene had state stored, restore state - showRestoredState(for: data) + showRestoredState(for: data, sessionIdentifier: sessionIdentifier) } - func showRestoredState(for data: RestoredStateData) { + func showRestoredState(for data: RestoredStateData, sessionIdentifier: String) { guard let openItemsController = controllers.userControllers?.openItemsController else { return } DDLogInfo("AppCoordinator: show restored state") guard let mainController = window.rootViewController as? MainViewController else { @@ -205,7 +205,7 @@ extension AppCoordinator: AppDelegateCoordinatorDelegate { collection = Collection(custom: .all) } mainController.showItems(for: collection, in: library) - openItemsController.restoreMostRecentlyOpenedItem(using: self) + openItemsController.restoreMostRecentlyOpenedItem(using: self, sessionIdentifier: sessionIdentifier) func loadRestoredStateData(libraryId: LibraryIdentifier, collectionId: CollectionIdentifier) -> (Library, Collection?)? { guard let dbStorage = controllers.userControllers?.dbStorage else { return nil } diff --git a/Zotero/Scenes/Detail/DetailCoordinator.swift b/Zotero/Scenes/Detail/DetailCoordinator.swift index fbd8a0c5b..7a99e2e2f 100644 --- a/Zotero/Scenes/Detail/DetailCoordinator.swift +++ b/Zotero/Scenes/Detail/DetailCoordinator.swift @@ -85,17 +85,27 @@ final class DetailCoordinator: Coordinator { let collection: Collection let library: Library let searchItemKeys: [String]? + let sessionIdentifier: String private unowned let controllers: Controllers private let disposeBag: DisposeBag private weak var citationNavigationController: UINavigationController? - init(library: Library, collection: Collection, searchItemKeys: [String]?, navigationController: UINavigationController, itemsTagFilterDelegate: ItemsTagFilterDelegate?, controllers: Controllers) { + init( + library: Library, + collection: Collection, + searchItemKeys: [String]?, + navigationController: UINavigationController, + itemsTagFilterDelegate: ItemsTagFilterDelegate?, + sessionIdentifier: String, + controllers: Controllers + ) { self.library = library self.collection = collection self.searchItemKeys = searchItemKeys self.navigationController = navigationController self.itemsTagFilterDelegate = itemsTagFilterDelegate + self.sessionIdentifier = sessionIdentifier self.controllers = controllers self.childCoordinators = [] self.disposeBag = DisposeBag() @@ -155,7 +165,7 @@ final class DetailCoordinator: Coordinator { remoteDownloadBatchData: remoteDownloadBatchData, identifierLookupBatchData: identifierLookupBatchData, error: nil, - openItemsCount: openItemsController.items.count + openItemsCount: openItemsController.getItems(for: sessionIdentifier).count ) let handler = ItemsActionHandler( dbStorage: dbStorage, @@ -300,7 +310,7 @@ final class DetailCoordinator: Coordinator { private func showPDF(at url: URL, key: String, library: Library) { guard let navigationController else { return } - controllers.userControllers?.openItemsController.open(.pdf(libraryId: library.identifier, key: key)) + controllers.userControllers?.openItemsController.open(.pdf(libraryId: library.identifier, key: key), for: sessionIdentifier) let viewControllerProvider: () -> UIViewController = { self.createPDFController(key: key, library: library, url: url) @@ -471,6 +481,7 @@ extension DetailCoordinator: DetailItemsCoordinatorDelegate { title: title, saveCallback: saveCallback, navigationController: navigationController, + sessionIdentifier: sessionIdentifier, controllers: controllers ) coordinator.parentCoordinator = self @@ -921,7 +932,9 @@ extension DetailCoordinator: DetailNoteEditorCoordinatorDelegate { switch result { case .success(let note): // If indeed a new note is created inform open items controller about it. - self?.controllers.userControllers?.openItemsController.open(.note(libraryId: library.identifier, key: note.key)) + if let self { + controllers.userControllers?.openItemsController.open(.note(libraryId: library.identifier, key: note.key), for: sessionIdentifier) + } case .failure: break @@ -932,7 +945,7 @@ extension DetailCoordinator: DetailNoteEditorCoordinatorDelegate { case .edit(let key), .readOnly(let key): DDLogInfo("DetailCoordinator: show note \(key)") - controllers.userControllers?.openItemsController.open(.note(libraryId: library.identifier, key: key)) + controllers.userControllers?.openItemsController.open(.note(libraryId: library.identifier, key: key), for: sessionIdentifier) } let viewControllerProvider: () -> UIViewController = { diff --git a/Zotero/Scenes/Detail/Items/Views/ItemsViewController.swift b/Zotero/Scenes/Detail/Items/Views/ItemsViewController.swift index 6bdace281..ab25318ef 100644 --- a/Zotero/Scenes/Detail/Items/Views/ItemsViewController.swift +++ b/Zotero/Scenes/Detail/Items/Views/ItemsViewController.swift @@ -653,8 +653,8 @@ final class ItemsViewController: UIViewController { image = UIImage(systemName: "0.square") accessibilityLabel = L10n.Items.restoreOpen action = { [weak self] _ in - guard let self, let presenter, let controller = controllers.userControllers?.openItemsController else { return } - controller.restoreMostRecentlyOpenedItem(using: presenter) + guard let self, let presenter, let controller = controllers.userControllers?.openItemsController, let sessionIdentifier = view.scene?.session.persistentIdentifier else { return } + controller.restoreMostRecentlyOpenedItem(using: presenter, sessionIdentifier: sessionIdentifier) } } @@ -718,8 +718,8 @@ final class ItemsViewController: UIViewController { } private func setupOpenItemsObserving() { - guard let controller = controllers.userControllers?.openItemsController else { return } - controller.observable + guard let controller = controllers.userControllers?.openItemsController, let sessionIdentifier = view.scene?.session.persistentIdentifier else { return } + controller.observable(for: sessionIdentifier) .observe(on: MainScheduler.instance) .subscribe(onNext: { [weak self] items in self?.viewModel.process(action: .updateOpenItems(items: items)) diff --git a/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift b/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift index 0db706e40..15c5f803a 100644 --- a/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift +++ b/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift @@ -112,12 +112,14 @@ class PDFReaderViewController: UIViewController { openItems.isEnabled = true openItems.accessibilityLabel = L10n.Accessibility.Pdf.openItems openItems.title = L10n.Accessibility.Pdf.openItems - let deferredOpenItemsMenuElement = openItemsController.deferredOpenItemsMenuElement(disableOpenItem: true) { [weak self] item, _ in - guard let self, let coordinatorDelegate else { return } - openItemsController.restore(item, using: coordinatorDelegate) + if let sessionIdentifier = view.scene?.session.persistentIdentifier { + let deferredOpenItemsMenuElement = openItemsController.deferredOpenItemsMenuElement(for: sessionIdentifier, disableOpenItem: true) { [weak self] item, _ in + guard let self, let coordinatorDelegate else { return } + openItemsController.restore(item, using: coordinatorDelegate) + } + let openItemsMenu = UIMenu(title: "Open Items", options: [.displayInline], children: [deferredOpenItemsMenuElement]) + openItems.menu = UIMenu(children: [openItemsMenu]) } - let openItemsMenu = UIMenu(title: "Open Items", options: [.displayInline], children: [deferredOpenItemsMenuElement]) - openItems.menu = UIMenu(children: [openItemsMenu]) return openItems }() private lazy var settingsButton: UIBarButtonItem = { @@ -210,7 +212,9 @@ class PDFReaderViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - self.set(userActivity: .pdfActivity(with: openItemsController.items, libraryId: self.viewModel.state.library.identifier, collectionId: Defaults.shared.selectedCollectionId)) + if let sessionIdentifier = view.scene?.session.persistentIdentifier { + self.set(userActivity: .pdfActivity(with: openItemsController.getItems(for: sessionIdentifier), libraryId: self.viewModel.state.library.identifier, collectionId: Defaults.shared.selectedCollectionId)) + } self.view.backgroundColor = .systemGray6 self.setupViews() @@ -1242,9 +1246,9 @@ class PDFReaderViewController: UIViewController { private var rightBarButtonItems: [UIBarButtonItem] { var buttons = [settingsButton, shareButton, searchButton] - if openItemsController.items.count > 1 { + if let sessionIdentifier = view.scene?.session.persistentIdentifier, openItemsController.getItems(for: sessionIdentifier).count > 1 { buttons.insert(openItemsButton, at: 1) - openItemsButton.image = .init(systemName: "\(openItemsController.items.count).square") + openItemsButton.image = .init(systemName: "\(openItemsController.getItems(for: sessionIdentifier).count).square") } if self.viewModel.state.library.metadataEditable { diff --git a/Zotero/Scenes/General/NoteEditorCoordinator.swift b/Zotero/Scenes/General/NoteEditorCoordinator.swift index 7febf7eef..2ea98ad81 100644 --- a/Zotero/Scenes/General/NoteEditorCoordinator.swift +++ b/Zotero/Scenes/General/NoteEditorCoordinator.swift @@ -35,6 +35,7 @@ final class NoteEditorCoordinator: NSObject, Coordinator { private let title: NoteEditorState.TitleData? private let library: Library private let saveCallback: NoteEditorSaveCallback + private let sessionIdentifier: String private unowned let controllers: Controllers init( @@ -45,6 +46,7 @@ final class NoteEditorCoordinator: NSObject, Coordinator { title: NoteEditorState.TitleData?, saveCallback: @escaping NoteEditorSaveCallback, navigationController: NavigationViewController, + sessionIdentifier: String, controllers: Controllers ) { self.kind = kind @@ -54,6 +56,7 @@ final class NoteEditorCoordinator: NSObject, Coordinator { self.library = library self.saveCallback = saveCallback self.navigationController = navigationController + self.sessionIdentifier = sessionIdentifier self.controllers = controllers childCoordinators = [] @@ -72,7 +75,7 @@ final class NoteEditorCoordinator: NSObject, Coordinator { func start(animated: Bool) { guard let dbStorage = controllers.userControllers?.dbStorage, let openItemsController = controllers.userControllers?.openItemsController else { return } - let state = NoteEditorState(kind: kind, library: library, title: title, text: initialText, tags: initialTags, openItemsCount: openItemsController.items.count) + let state = NoteEditorState(kind: kind, library: library, title: title, text: initialText, tags: initialTags, openItemsCount: openItemsController.getItems(for: sessionIdentifier).count) let handler = NoteEditorActionHandler(dbStorage: dbStorage, schemaController: controllers.schemaController, saveCallback: saveCallback) let viewModel = ViewModel(initialState: state, handler: handler) let controller = NoteEditorViewController(viewModel: viewModel, openItemsController: openItemsController) diff --git a/Zotero/Scenes/General/Views/NoteEditorViewController.swift b/Zotero/Scenes/General/Views/NoteEditorViewController.swift index 9f32e2092..448141755 100644 --- a/Zotero/Scenes/General/Views/NoteEditorViewController.swift +++ b/Zotero/Scenes/General/Views/NoteEditorViewController.swift @@ -52,7 +52,9 @@ final class NoteEditorViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - set(userActivity: .pdfActivity(with: openItemsController.items, libraryId: viewModel.state.library.identifier, collectionId: Defaults.shared.selectedCollectionId)) + if let sessionIdentifier = view.scene?.session.persistentIdentifier { + set(userActivity: .pdfActivity(with: openItemsController.getItems(for: sessionIdentifier), libraryId: viewModel.state.library.identifier, collectionId: Defaults.shared.selectedCollectionId)) + } if let data = viewModel.state.title { navigationItem.titleView = NoteEditorTitleView(type: data.type, title: data.title) @@ -101,16 +103,25 @@ final class NoteEditorViewController: UIViewController { item = done case .restoreOpenItems: - let openItems = UIBarButtonItem(image: UIImage(systemName: "\(openItemsController.items.count).square"), style: .plain, target: nil, action: nil) - openItems.isEnabled = true + let openItems: UIBarButtonItem + if let sessionIdentifier = view.scene?.session.persistentIdentifier { + let items = openItemsController.getItems(for: sessionIdentifier) + openItems = UIBarButtonItem(image: UIImage(systemName: "\(items.count).square"), style: .plain, target: nil, action: nil) + openItems.isEnabled = true + if let sessionIdentifier = view.scene?.session.persistentIdentifier { + let deferredOpenItemsMenuElement = openItemsController.deferredOpenItemsMenuElement(for: sessionIdentifier, disableOpenItem: true) { [weak self] item, _ in + guard let self, let coordinatorDelegate else { return } + openItemsController.restore(item, using: coordinatorDelegate) + } + let openItemsMenu = UIMenu(title: "Open Items", options: [.displayInline], children: [deferredOpenItemsMenuElement]) + openItems.menu = UIMenu(children: [openItemsMenu]) + } + } else { + openItems = UIBarButtonItem(image: UIImage(systemName: "0.square"), style: .plain, target: nil, action: nil) + openItems.isEnabled = false + } openItems.accessibilityLabel = L10n.Accessibility.Pdf.openItems openItems.title = L10n.Accessibility.Pdf.openItems - let deferredOpenItemsMenuElement = openItemsController.deferredOpenItemsMenuElement(disableOpenItem: true) { [weak self] item, _ in - guard let self, let coordinatorDelegate else { return } - openItemsController.restore(item, using: coordinatorDelegate) - } - let openItemsMenu = UIMenu(title: "Open Items", options: [.displayInline], children: [deferredOpenItemsMenuElement]) - openItems.menu = UIMenu(children: [openItemsMenu]) item = openItems } @@ -140,7 +151,8 @@ final class NoteEditorViewController: UIViewController { } func setupOpenItemsObserving() { - openItemsController.observable + guard let sessionIdentifier = view.scene?.session.persistentIdentifier else { return } + openItemsController.observable(for: sessionIdentifier) .observe(on: MainScheduler.instance) .subscribe(onNext: { [weak self] items in self?.viewModel.process(action: .updateOpenItems(items: items)) diff --git a/Zotero/Scenes/Main/Views/MainViewController.swift b/Zotero/Scenes/Main/Views/MainViewController.swift index 7646d3d81..731ece10f 100644 --- a/Zotero/Scenes/Main/Views/MainViewController.swift +++ b/Zotero/Scenes/Main/Views/MainViewController.swift @@ -24,6 +24,7 @@ protocol MainCoordinatorSyncToolbarDelegate: AnyObject { final class MainViewController: UISplitViewController { // Constants + private let sessionIdentifier: String private let controllers: Controllers private let disposeBag: DisposeBag // Variables @@ -42,7 +43,8 @@ final class MainViewController: UISplitViewController { // MARK: - Lifecycle - init(controllers: Controllers) { + init(sessionIdentifier: String, controllers: Controllers) { + self.sessionIdentifier = sessionIdentifier self.controllers = controllers self.disposeBag = DisposeBag() @@ -75,7 +77,7 @@ final class MainViewController: UISplitViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - self.set(userActivity: .mainActivity(with: controllers.userControllers?.openItemsController.items ?? [])) + self.set(userActivity: .mainActivity(with: controllers.userControllers?.openItemsController.getItems(for: sessionIdentifier) ?? [])) self.didAppear = true } @@ -97,6 +99,7 @@ final class MainViewController: UISplitViewController { searchItemKeys: searchItemKeys, navigationController: navigationController, itemsTagFilterDelegate: tagFilterController, + sessionIdentifier: sessionIdentifier, controllers: self.controllers ) coordinator.start(animated: false)