From 3cdd33d8a41999d1ee683e96faf775fc0f27b3c7 Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Thu, 31 Oct 2024 16:05:34 +0200 Subject: [PATCH] Improve item details collection view notes section performance --- .../ItemDetail/Models/ItemDetailAction.swift | 2 +- .../ViewModels/ItemDetailActionHandler.swift | 12 +++---- .../ItemDetailCollectionViewHandler.swift | 36 +++++++++---------- .../ItemDetail/Views/ItemDetailNoteCell.swift | 4 +-- .../Views/ItemDetailNoteContentView.swift | 4 +-- .../Views/ItemDetailViewController.swift | 4 +-- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Zotero/Scenes/Detail/ItemDetail/Models/ItemDetailAction.swift b/Zotero/Scenes/Detail/ItemDetail/Models/ItemDetailAction.swift index b4b486e71..df99f805b 100644 --- a/Zotero/Scenes/Detail/ItemDetail/Models/ItemDetailAction.swift +++ b/Zotero/Scenes/Detail/ItemDetail/Models/ItemDetailAction.swift @@ -17,7 +17,7 @@ enum ItemDetailAction { case deleteAttachmentFile(Attachment) case deleteAttachment(Attachment) case deleteCreator(String) - case deleteNote(Note) + case deleteNote(key: String) case deleteTag(Tag) case endEditing case loadInitialData diff --git a/Zotero/Scenes/Detail/ItemDetail/ViewModels/ItemDetailActionHandler.swift b/Zotero/Scenes/Detail/ItemDetail/ViewModels/ItemDetailActionHandler.swift index 35a6e287c..d3138e12f 100644 --- a/Zotero/Scenes/Detail/ItemDetail/ViewModels/ItemDetailActionHandler.swift +++ b/Zotero/Scenes/Detail/ItemDetail/ViewModels/ItemDetailActionHandler.swift @@ -139,8 +139,8 @@ struct ItemDetailActionHandler: ViewModelActionHandler, BackgroundDbProcessingAc case .deleteTag(let tag): self.delete(tag: tag, in: viewModel) - case .deleteNote(let note): - self.delete(note: note, in: viewModel) + case .deleteNote(let key): + self.deleteNote(key: key, in: viewModel) case .deleteAttachment(let attachment): self.delete(attachment: attachment, in: viewModel) @@ -370,10 +370,10 @@ struct ItemDetailActionHandler: ViewModelActionHandler, BackgroundDbProcessingAc } } - private func delete(note: Note, in viewModel: ViewModel) { - guard viewModel.state.notes.contains(note) else { return } - self.trashItem(key: note.key, reloadType: .section(.notes), in: viewModel) { state in - guard let index = viewModel.state.notes.firstIndex(of: note) else { return } + private func deleteNote(key: String, in viewModel: ViewModel) { + guard viewModel.state.notes.contains(where: { $0.key == key }) else { return } + trashItem(key: key, reloadType: .section(.notes), in: viewModel) { state in + guard let index = viewModel.state.notes.firstIndex(where: { $0.key == key }) else { return } state.notes.remove(at: index) } } diff --git a/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailCollectionViewHandler.swift b/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailCollectionViewHandler.swift index e47537fea..310e40ad3 100644 --- a/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailCollectionViewHandler.swift +++ b/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailCollectionViewHandler.swift @@ -22,7 +22,7 @@ final class ItemDetailCollectionViewHandler: NSObject { enum Action { case openCreatorCreation case openCreatorEditor(ItemDetailState.Creator) - case openNoteEditor(Note?) + case openNoteEditor(key: String?) case openTagPicker case openTypePicker case openFilePicker @@ -61,7 +61,7 @@ final class ItemDetailCollectionViewHandler: NSObject { case dateAdded(Date) case dateModified(Date) case field(key: String, multiline: Bool) - case note(note: Note, isProcessing: Bool) + case note(key: String, title: String, isProcessing: Bool) case tag(id: UUID, tag: Tag, isProcessing: Bool) case title case type(String) @@ -227,8 +227,8 @@ final class ItemDetailCollectionViewHandler: NSObject { } return collectionView.dequeueConfiguredReusableCell(using: fieldEditRegistration, for: indexPath, item: (field, titleWidth)) - case .note(let note, let isProcessing): - return collectionView.dequeueConfiguredReusableCell(using: noteRegistration, for: indexPath, item: (note, isProcessing)) + case .note(let key, let title, let isProcessing): + return collectionView.dequeueConfiguredReusableCell(using: noteRegistration, for: indexPath, item: (key, title, isProcessing)) case .tag(_, let tag, let isProcessing): return collectionView.dequeueConfiguredReusableCell(using: tagRegistration, for: indexPath, item: (tag, isProcessing)) @@ -356,7 +356,7 @@ final class ItemDetailCollectionViewHandler: NSObject { case .attachment(_, let type) where type != .disabled: title = L10n.moveToTrash - case .note(_, let isProcessing) where !isProcessing: + case .note(_, _, let isProcessing) where !isProcessing: title = L10n.moveToTrash case .tag(_, _, let isProcessing) where !isProcessing: @@ -389,8 +389,8 @@ final class ItemDetailCollectionViewHandler: NSObject { case .attachment(let attachment, _): self.viewModel.process(action: .deleteAttachment(attachment)) - case .note(let note, _): - self.viewModel.process(action: .deleteNote(note)) + case .note(let key, _, _): + self.viewModel.process(action: .deleteNote(key: key)) case .title, .abstract, .addAttachment, .addCreator, .addNote, .addTag, .dateAdded, .dateModified, .type, .field: break @@ -620,7 +620,7 @@ final class ItemDetailCollectionViewHandler: NSObject { for _row in dataSource.snapshot().itemIdentifiers { switch _row { - case .note(let note, _) where note.key == itemKey: + case .note(let key, _, _) where key == itemKey: row = _row case .attachment(let attachment, _) where attachment.key == itemKey: @@ -674,7 +674,7 @@ final class ItemDetailCollectionViewHandler: NSObject { case .notes: let notes: [Row] = state.notes.map({ note in let isProcessing = state.backgroundProcessedItems.contains(note.key) - return .note(note: note, isProcessing: isProcessing) + return .note(key: note.key, title: note.title, isProcessing: isProcessing) }) if state.library.metadataEditable { return notes + [.addNote] @@ -836,10 +836,10 @@ final class ItemDetailCollectionViewHandler: NSObject { } }() - private lazy var noteRegistration: UICollectionView.CellRegistration = { + private lazy var noteRegistration: UICollectionView.CellRegistration = { return UICollectionView.CellRegistration { [weak self] cell, indexPath, data in guard let self else { return } - cell.contentConfiguration = ItemDetailNoteCell.ContentConfiguration(note: data.0, isProcessing: data.1, layoutMargins: layoutMargins(for: indexPath, self: self)) + cell.contentConfiguration = ItemDetailNoteCell.ContentConfiguration(title: data.title, isProcessing: data.isProcessing, layoutMargins: layoutMargins(for: indexPath, self: self)) } }() @@ -878,7 +878,7 @@ extension ItemDetailCollectionViewHandler: UICollectionViewDelegate { switch row { case .addNote: - observer.on(.next(.openNoteEditor(nil))) + observer.on(.next(.openNoteEditor(key: nil))) case .addAttachment: observer.on(.next(.openFilePicker)) @@ -901,9 +901,9 @@ extension ItemDetailCollectionViewHandler: UICollectionViewDelegate { guard viewModel.state.isEditing else { return } observer.on(.next(.openCreatorEditor(creator))) - case .note(let note, let isProcessing): + case .note(let key, let title, let isProcessing): guard !isProcessing else { return } - observer.on(.next(.openNoteEditor(note))) + observer.on(.next(.openNoteEditor(key: key))) case .type: guard viewModel.state.isEditing && !viewModel.state.data.isAttachment else { return } @@ -964,8 +964,8 @@ extension ItemDetailCollectionViewHandler: UICollectionViewDelegate { case .tag(_, let tag, _): menu = createContextMenu(for: tag) - case .note(let note, _): - menu = createContextMenu(for: note) + case .note(let key, _, _): + menu = createContextMenuForNote(key: key) default: return nil @@ -995,10 +995,10 @@ extension ItemDetailCollectionViewHandler: UICollectionViewDelegate { return UIMenu(title: "", children: actions) } - func createContextMenu(for note: Note) -> UIMenu? { + func createContextMenuForNote(key: String) -> UIMenu? { var actions: [UIAction] = [] actions.append(UIAction(title: L10n.moveToTrash, image: UIImage(systemName: "trash"), attributes: .destructive) { [weak self] _ in - self?.viewModel.process(action: .deleteNote(note)) + self?.viewModel.process(action: .deleteNote(key: key)) }) return UIMenu(title: "", children: actions) } diff --git a/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailNoteCell.swift b/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailNoteCell.swift index b7214153c..4a332ef2b 100644 --- a/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailNoteCell.swift +++ b/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailNoteCell.swift @@ -10,7 +10,7 @@ import UIKit final class ItemDetailNoteCell: UICollectionViewListCell { struct ContentConfiguration: UIContentConfiguration { - let note: Note + let title: String let isProcessing: Bool let layoutMargins: UIEdgeInsets @@ -51,7 +51,7 @@ final class ItemDetailNoteCell: UICollectionViewListCell { private func apply(configuration: ContentConfiguration) { self.contentView.layoutMargins = configuration.layoutMargins - self.contentView.setup(with: configuration.note, isProcessing: configuration.isProcessing) + self.contentView.setup(with: configuration.title, isProcessing: configuration.isProcessing) } } } diff --git a/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailNoteContentView.swift b/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailNoteContentView.swift index 55befc48a..3fda59169 100644 --- a/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailNoteContentView.swift +++ b/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailNoteContentView.swift @@ -19,11 +19,11 @@ class ItemDetailNoteContentView: UIView { self.heightAnchor.constraint(greaterThanOrEqualToConstant: ItemDetailLayout.minCellHeight).isActive = true } - func setup(with note: Note, isProcessing: Bool) { + func setup(with title: String, isProcessing: Bool) { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.minimumLineHeight = ItemDetailLayout.lineHeight paragraphStyle.maximumLineHeight = ItemDetailLayout.lineHeight - let attributedString = NSAttributedString(string: note.title, attributes: [.font: UIFont.preferredFont(forTextStyle: .body), .paragraphStyle: paragraphStyle]) + let attributedString = NSAttributedString(string: title, attributes: [.font: UIFont.preferredFont(forTextStyle: .body), .paragraphStyle: paragraphStyle]) self.label.attributedText = attributedString self.label.textColor = isProcessing ? .systemGray2 : .label diff --git a/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailViewController.swift b/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailViewController.swift index 91c26d347..28beaa778 100644 --- a/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailViewController.swift +++ b/Zotero/Scenes/Detail/ItemDetail/Views/ItemDetailViewController.swift @@ -175,14 +175,14 @@ final class ItemDetailViewController: UIViewController { self?.viewModel.process(action: .addAttachments(urls)) }) - case .openNoteEditor(let note): + case .openNoteEditor(let key): let library = viewModel.state.library var kind: NoteEditorKind = .itemCreation(parentKey: viewModel.state.key) var text: String = "" var tags: [Tag] = [] var title: String? let parentTitleData = NoteEditorState.TitleData(type: viewModel.state.data.type, title: viewModel.state.data.title) - if let note { + if let note = viewModel.state.notes.first(where: { $0.key == key }) { if library.metadataEditable { kind = .edit(key: note.key) } else {