diff --git a/Zotero/Controllers/Database/Requests/CreateHtmlEpubAnnotationsDbRequest.swift b/Zotero/Controllers/Database/Requests/CreateHtmlEpubAnnotationsDbRequest.swift index c506daeb8..7fa82d4cb 100644 --- a/Zotero/Controllers/Database/Requests/CreateHtmlEpubAnnotationsDbRequest.swift +++ b/Zotero/Controllers/Database/Requests/CreateHtmlEpubAnnotationsDbRequest.swift @@ -21,17 +21,17 @@ struct CreateHtmlEpubAnnotationsDbRequest: DbRequest { var needsWrite: Bool { return true } func process(in database: Realm) throws { - guard let parent = database.objects(RItem.self).filter(.key(self.attachmentKey, in: self.libraryId)).first else { return } + guard let parent = database.objects(RItem.self).filter(.key(attachmentKey, in: libraryId)).first else { return } - for annotation in self.annotations { - self.create(annotation: annotation, parent: parent, in: database) + for annotation in annotations { + create(annotation: annotation, parent: parent, in: database) } } private func create(annotation: HtmlEpubAnnotation, parent: RItem, in database: Realm) { let item: RItem - if let _item = database.objects(RItem.self).filter(.key(annotation.key, in: self.libraryId)).first { + if let _item = database.objects(RItem.self).filter(.key(annotation.key, in: libraryId)).first { if !_item.deleted { // If item exists and is not deleted locally, we can ignore this request return @@ -45,8 +45,8 @@ struct CreateHtmlEpubAnnotationsDbRequest: DbRequest { item = RItem() item.key = annotation.key item.rawType = ItemTypes.annotation - item.localizedType = self.schemaController.localized(itemType: ItemTypes.annotation) ?? "" - item.libraryId = self.libraryId + item.localizedType = schemaController.localized(itemType: ItemTypes.annotation) ?? "" + item.libraryId = libraryId item.dateAdded = annotation.dateCreated database.add(item) } @@ -58,11 +58,11 @@ struct CreateHtmlEpubAnnotationsDbRequest: DbRequest { item.parent = parent if annotation.isAuthor { - item.createdBy = database.object(ofType: RUser.self, forPrimaryKey: self.userId) + item.createdBy = database.object(ofType: RUser.self, forPrimaryKey: userId) } // We need to submit tags on creation even if they are empty, so we need to mark them as changed - self.addFields(for: annotation, to: item, database: database) + addFields(for: annotation, to: item, database: database) let changes: RItemChanges = [.parent, .fields, .type, .tags] item.changes.append(RObjectChange.create(changes: changes)) } diff --git a/Zotero/Scenes/Detail/DetailCoordinator.swift b/Zotero/Scenes/Detail/DetailCoordinator.swift index 2b9608b06..5ba3813d2 100644 --- a/Zotero/Scenes/Detail/DetailCoordinator.swift +++ b/Zotero/Scenes/Detail/DetailCoordinator.swift @@ -182,7 +182,7 @@ final class DetailCoordinator: Coordinator { private func show(attachment: Attachment, parentKey: String?, library: Library, sourceView: UIView, sourceRect: CGRect?) { switch attachment.type { case .url(let url): - self.show(url: url) + show(url: url) case .file(let filename, let contentType, _, _): let file = Files.attachmentFile(in: library.identifier, key: attachment.key, filename: filename, contentType: contentType) @@ -192,39 +192,39 @@ final class DetailCoordinator: Coordinator { switch contentType { case "application/pdf": DDLogInfo("DetailCoordinator: show PDF \(attachment.key)") - self.showPdf(at: url, key: attachment.key, parentKey: parentKey, library: library) + showPdf(at: url, key: attachment.key, parentKey: parentKey, library: library) case "text/html", "application/epub+zip": DDLogInfo("DetailCoordinator: show HTML / EPUB \(attachment.key)") - self.showHtmlEpubReader(for: url, key: attachment.key, library: library) + showHtmlEpubReader(for: url, key: attachment.key, library: library) case "text/plain": let text = try? String(contentsOf: url, encoding: .utf8) if let text = text { DDLogInfo("DetailCoordinator: show plain text \(attachment.key)") - self.show(text: text, title: filename) + show(text: text, title: filename) } else { DDLogInfo("DetailCoordinator: share plain text \(attachment.key)") - self.share(item: url, sourceView: .view(sourceView, rect)) + share(item: url, sourceView: .view(sourceView, rect)) } case _ where contentType.contains("image"): let image = (contentType == "image/gif") ? (try? Data(contentsOf: url)).flatMap({ try? UIImage(gifData: $0) }) : UIImage(contentsOfFile: url.path) if let image = image { DDLogInfo("DetailCoordinator: show image \(attachment.key)") - self.show(image: image, title: filename) + show(image: image, title: filename) } else { DDLogInfo("DetailCoordinator: share image \(attachment.key)") - self.share(item: url, sourceView: .view(sourceView, rect)) + share(item: url, sourceView: .view(sourceView, rect)) } default: if AVURLAsset(url: url).isPlayable { DDLogInfo("DetailCoordinator: show video \(attachment.key)") - self.showVideo(for: url) + showVideo(for: url) } else { DDLogInfo("DetailCoordinator: share attachment \(attachment.key)") - self.share(item: file.createUrl(), sourceView: .view(sourceView, rect)) + share(item: file.createUrl(), sourceView: .view(sourceView, rect)) } } } diff --git a/Zotero/Scenes/Detail/HTML:EPUB/HtmlEpubCoordinator.swift b/Zotero/Scenes/Detail/HTML:EPUB/HtmlEpubCoordinator.swift index 1196b117f..4a0fe22a6 100644 --- a/Zotero/Scenes/Detail/HTML:EPUB/HtmlEpubCoordinator.swift +++ b/Zotero/Scenes/Detail/HTML:EPUB/HtmlEpubCoordinator.swift @@ -61,11 +61,12 @@ final class HtmlEpubCoordinator: Coordinator { self.url = url self.navigationController = navigationController self.controllers = controllers - self.childCoordinators = [] - self.disposeBag = DisposeBag() + childCoordinators = [] + disposeBag = DisposeBag() - navigationController.dismissHandler = { - self.parentCoordinator?.childDidFinish(self) + navigationController.dismissHandler = { [weak self] in + guard let self else { return } + parentCoordinator?.childDidFinish(self) } } @@ -75,10 +76,10 @@ final class HtmlEpubCoordinator: Coordinator { func start(animated: Bool) { let username = Defaults.shared.username - guard let dbStorage = self.controllers.userControllers?.dbStorage, - let userId = self.controllers.sessionController.sessionData?.userId, + guard let dbStorage = controllers.userControllers?.dbStorage, + let userId = controllers.sessionController.sessionData?.userId, !username.isEmpty, - let parentNavigationController = self.parentCoordinator?.navigationController + let parentNavigationController = parentCoordinator?.navigationController else { return } let handler = HtmlEpubReaderActionHandler( @@ -94,8 +95,7 @@ final class HtmlEpubCoordinator: Coordinator { compactSize: UIDevice.current.isCompactWidth(size: parentNavigationController.view.frame.size) ) controller.coordinatorDelegate = self - - self.navigationController?.setViewControllers([controller], animated: false) + navigationController?.setViewControllers([controller], animated: false) } } @@ -128,7 +128,7 @@ extension HtmlEpubCoordinator: HtmlEpubReaderCoordinatorDelegate { let controller = UIAlertController(title: title, message: message, preferredStyle: .alert) controller.addAction(UIAlertAction(title: L10n.ok, style: .default)) - self.navigationController?.present(controller, animated: true) + navigationController?.present(controller, animated: true) } func showToolSettings(tool: AnnotationTool, colorHex: String?, sizeValue: Float?, sender: SourceView, userInterfaceStyle: UIUserInterfaceStyle, valueChanged: @escaping (String?, Float?) -> Void) { @@ -141,6 +141,7 @@ extension HtmlEpubCoordinator: HtmlEpubReaderCoordinatorDelegate { case .pad: controller.overrideUserInterfaceStyle = userInterfaceStyle controller.modalPresentationStyle = .popover + switch sender { case .view(let view, _): controller.popoverPresentationController?.sourceView = view @@ -148,7 +149,7 @@ extension HtmlEpubCoordinator: HtmlEpubReaderCoordinatorDelegate { case .item(let item): controller.popoverPresentationController?.barButtonItem = item } - self.navigationController?.present(controller, animated: true, completion: nil) + navigationController?.present(controller, animated: true, completion: nil) default: let navigationController = UINavigationController(rootViewController: controller) @@ -162,7 +163,7 @@ extension HtmlEpubCoordinator: HtmlEpubReaderCoordinatorDelegate { extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate { func showTagPicker(libraryId: LibraryIdentifier, selected: Set, userInterfaceStyle: UIUserInterfaceStyle?, picked: @escaping ([Tag]) -> Void) { guard let navigationController else { return } - (self.parentCoordinator as? DetailCoordinator)?.showTagPicker( + (parentCoordinator as? DetailCoordinator)?.showTagPicker( libraryId: libraryId, selected: selected, userInterfaceStyle: userInterfaceStyle, @@ -184,7 +185,7 @@ extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate { navigationController.overrideUserInterfaceStyle = userInterfaceStyle let coordinator = AnnotationEditCoordinator( - data: AnnotationEditState.AnnotationData( + data: AnnotationEditState.Data( type: annotation.type, isEditable: annotation.editability(currentUserId: userId, library: library) == .editable, color: annotation.color, @@ -195,10 +196,10 @@ extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate { saveAction: saveAction, deleteAction: deleteAction, navigationController: navigationController, - controllers: self.controllers + controllers: controllers ) coordinator.parentCoordinator = self - self.childCoordinators.append(coordinator) + childCoordinators.append(coordinator) coordinator.start(animated: false) if UIDevice.current.userInterfaceIdiom == .pad { @@ -216,21 +217,19 @@ extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate { popoverDelegate: UIPopoverPresentationControllerDelegate, userInterfaceStyle: UIUserInterfaceStyle ) -> PublishSubject? { - guard let currentNavigationController = self.navigationController, let annotation = viewModel.state.selectedAnnotationKey.flatMap({ viewModel.state.annotations[$0] }) else { return nil } + guard let currentNavigationController = navigationController, let annotation = viewModel.state.selectedAnnotationKey.flatMap({ viewModel.state.annotations[$0] }) else { return nil } DDLogInfo("HtmlEpubCoordinator: show annotation popover") - if let coordinator = self.childCoordinators.last, coordinator is AnnotationPopoverCoordinator { + if let coordinator = childCoordinators.last, coordinator is AnnotationPopoverCoordinator { return nil } let navigationController = NavigationViewController() navigationController.overrideUserInterfaceStyle = userInterfaceStyle - let author = viewModel.state.library.identifier == .custom(.myLibrary) ? "" : annotation.author let comment = viewModel.state.comments[annotation.key] ?? NSAttributedString() let editability = annotation.editability(currentUserId: viewModel.state.userId, library: viewModel.state.library) - let data = AnnotationPopoverState.Data( libraryId: viewModel.state.library.identifier, type: annotation.type, @@ -246,7 +245,7 @@ extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate { ) let coordinator = AnnotationPopoverCoordinator(data: data, navigationController: navigationController, controllers: self.controllers) coordinator.parentCoordinator = self - self.childCoordinators.append(coordinator) + childCoordinators.append(coordinator) coordinator.start(animated: false) if UIDevice.current.userInterfaceIdiom == .pad { @@ -274,17 +273,16 @@ extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate { let navigationController = NavigationViewController() navigationController.overrideUserInterfaceStyle = userInterfaceStyle - let coordinator = AnnotationsFilterPopoverCoordinator( initialFilter: filter, availableColors: availableColors, availableTags: availableTags, navigationController: navigationController, - controllers: self.controllers, + controllers: controllers, completionHandler: completed ) coordinator.parentCoordinator = self - self.childCoordinators.append(coordinator) + childCoordinators.append(coordinator) coordinator.start(animated: false) if UIDevice.current.userInterfaceIdiom == .pad { @@ -301,20 +299,18 @@ extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate { let state = ReaderSettingsState(settings: settings) let viewModel = ViewModel(initialState: state, handler: ReaderSettingsActionHandler()) - let baseController = ReaderSettingsViewController(visibleRows: [.appearance, .sleep], viewModel: viewModel) - + let baseController = ReaderSettingsViewController(rows: [.appearance, .sleep], viewModel: viewModel) let controller: UIViewController if UIDevice.current.userInterfaceIdiom == .pad { controller = baseController } else { controller = UINavigationController(rootViewController: baseController) } - controller.modalPresentationStyle = UIDevice.current.userInterfaceIdiom == .pad ? .popover : .formSheet controller.popoverPresentationController?.barButtonItem = sender controller.preferredContentSize = CGSize(width: 480, height: 92) controller.overrideUserInterfaceStyle = settings.appearance.userInterfaceStyle - self.navigationController?.present(controller, animated: true, completion: nil) + navigationController?.present(controller, animated: true, completion: nil) return viewModel } diff --git a/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubAnnotation.swift b/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubAnnotation.swift index e3f9defcc..5cf859e97 100644 --- a/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubAnnotation.swift +++ b/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubAnnotation.swift @@ -50,7 +50,7 @@ struct HtmlEpubAnnotation { if !library.metadataEditable { return .notEditable } - return self.isAuthor ? .editable : .deletable + return isAuthor ? .editable : .deletable } } } diff --git a/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubReaderState.swift b/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubReaderState.swift index b619ae4c8..69c6329a4 100644 --- a/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubReaderState.swift +++ b/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubReaderState.swift @@ -97,19 +97,19 @@ struct HtmlEpubReaderState: ViewModelState { self.library = library self.userId = userId self.username = username - self.commentFont = PDFReaderLayout.annotationLayout.font - self.sortedKeys = [] - self.annotations = [:] - self.comments = [:] - self.sidebarEditingEnabled = false - self.selectedAnnotationCommentActive = false - self.toolColors = [ + commentFont = PDFReaderLayout.annotationLayout.font + sortedKeys = [] + annotations = [:] + comments = [:] + sidebarEditingEnabled = false + selectedAnnotationCommentActive = false + toolColors = [ .highlight: UIColor(hex: Defaults.shared.highlightColorHex), .note: UIColor(hex: Defaults.shared.noteColorHex) ] - self.changes = [] - self.deletionEnabled = false - self.selectedAnnotationsDuringEditing = [] + changes = [] + deletionEnabled = false + selectedAnnotationsDuringEditing = [] } mutating func cleanup() { diff --git a/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubSettings.swift b/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubSettings.swift index f5fa1594a..8c6116369 100644 --- a/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubSettings.swift +++ b/Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubSettings.swift @@ -25,13 +25,13 @@ extension HtmlEpubSettings: Codable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: Keys.self) let appearanceRaw = try container.decode(UInt.self, forKey: .appearance) - self.appearance = ReaderSettingsState.Appearance(rawValue: appearanceRaw) ?? .automatic + appearance = ReaderSettingsState.Appearance(rawValue: appearanceRaw) ?? .automatic // This setting is not persisted, always defaults to false - self.idleTimerDisabled = false + idleTimerDisabled = false } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: Keys.self) - try container.encode(self.appearance.rawValue, forKey: .appearance) + try container.encode(appearance.rawValue, forKey: .appearance) } } diff --git a/Zotero/Scenes/Detail/HTML:EPUB/ViewModels/HtmlEpubReaderActionHandler.swift b/Zotero/Scenes/Detail/HTML:EPUB/ViewModels/HtmlEpubReaderActionHandler.swift index 91d417bbe..4b3c5cbf5 100644 --- a/Zotero/Scenes/Detail/HTML:EPUB/ViewModels/HtmlEpubReaderActionHandler.swift +++ b/Zotero/Scenes/Detail/HTML:EPUB/ViewModels/HtmlEpubReaderActionHandler.swift @@ -28,7 +28,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro self.htmlAttributedStringConverter = htmlAttributedStringConverter self.dateParser = dateParser self.idleTimerController = idleTimerController - self.backgroundQueue = DispatchQueue(label: "org.zotero.Zotero.HtmlEpubReaderActionHandler.queue", qos: .userInteractive) + backgroundQueue = DispatchQueue(label: "org.zotero.Zotero.HtmlEpubReaderActionHandler.queue", qos: .userInteractive) } func process(action: HtmlEpubReaderAction, in viewModel: ViewModel) { @@ -54,7 +54,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro } case .selectAnnotationFromSidebar(let key): - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in _select(data: (key, nil), didSelectInDocument: false, state: &state) } @@ -75,7 +75,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro set(tags: tags, to: key, in: viewModel) case .deselectSelectedAnnotation: - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in _select(data: nil, didSelectInDocument: false, state: &state) } @@ -130,18 +130,18 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro } if settings.idleTimerDisabled { - self.idleTimerController.disable() + idleTimerController.disable() } else { - self.idleTimerController.enable() + idleTimerController.enable() } } private func set(settings: HtmlEpubSettings, in viewModel: ViewModel) { if viewModel.state.settings.idleTimerDisabled != settings.idleTimerDisabled { if settings.idleTimerDisabled { - self.idleTimerController.disable() + idleTimerController.disable() } else { - self.idleTimerController.enable() + idleTimerController.enable() } } @@ -187,7 +187,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro let annotationDeletable = annotation.editability(currentUserId: viewModel.state.userId, library: viewModel.state.library) != .notEditable - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in if state.selectedAnnotationsDuringEditing.isEmpty { state.deletionEnabled = annotationDeletable } else { @@ -200,7 +200,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro } private func deselectDuringEditing(key: String, in viewModel: ViewModel) { - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in state.selectedAnnotationsDuringEditing.remove(key) if state.selectedAnnotationsDuringEditing.isEmpty { @@ -249,8 +249,8 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro } let request = StorePageForItemDbRequest(key: viewModel.state.key, libraryId: viewModel.state.library.identifier, page: page) - self.perform(request: request) { error in - guard let error = error else { return } + perform(request: request) { error in + guard let error else { return } // TODO: - handle error DDLogError("HtmlEpubReaderActionHandler: can't store page - \(error)") } @@ -262,15 +262,13 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro guard !keys.isEmpty else { return } let request = MarkObjectsAsDeletedDbRequest(keys: keys, libraryId: viewModel.state.library.identifier) - self.perform(request: request) { [weak self, weak viewModel] error in - guard let self = self, let viewModel = viewModel else { return } + perform(request: request) { [weak self, weak viewModel] error in + guard let self, let error, let viewModel else { return } - if let error = error { - DDLogError("HtmlEpubReaderActionHandler: can't remove annotations \(keys) - \(error)") + DDLogError("HtmlEpubReaderActionHandler: can't remove annotations \(keys) - \(error)") - self.update(viewModel: viewModel) { state in - state.error = .cantDeleteAnnotation - } + update(viewModel: viewModel) { state in + state.error = .cantDeleteAnnotation } } } @@ -283,12 +281,12 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro KeyBaseKeyPair(key: FieldKeys.Item.Annotation.Position.lineWidth, baseKey: FieldKeys.Item.Annotation.position): "\(Decimal(lineWidth).rounded(to: 3))" ] let request = EditItemFieldsDbRequest(key: key, libraryId: viewModel.state.library.identifier, fieldValues: values, dateParser: dateParser) - self.perform(request: request) { [weak self, weak viewModel] error in - guard let error = error, let self = self, let viewModel = viewModel else { return } + perform(request: request) { [weak self, weak viewModel] error in + guard let error, let self, let viewModel else { return } DDLogError("HtmlEpubReaderActionHandler: can't update annotation \(key) - \(error)") - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in state.error = .cantUpdateAnnotation } } @@ -298,11 +296,11 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro let values = [KeyBaseKeyPair(key: FieldKeys.Item.Annotation.color, baseKey: nil): color] let request = EditItemFieldsDbRequest(key: key, libraryId: viewModel.state.library.identifier, fieldValues: values, dateParser: dateParser) self.perform(request: request) { [weak self, weak viewModel] error in - guard let error = error, let self = self, let viewModel = viewModel else { return } + guard let error, let self, let viewModel else { return } DDLogError("HtmlEpubReaderActionHandler: can't set color \(key) - \(error)") - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in state.error = .cantUpdateAnnotation } } @@ -317,12 +315,12 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro let values = [KeyBaseKeyPair(key: FieldKeys.Item.Annotation.comment, baseKey: nil): htmlComment] let request = EditItemFieldsDbRequest(key: key, libraryId: viewModel.state.library.identifier, fieldValues: values, dateParser: dateParser) - perform(request: request) { error in - guard let error else { return } + perform(request: request) { [weak self] error in + guard let self, let error else { return } DDLogError("HtmlEpubReaderActionHandler: can't set comment \(key) - \(error)") - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in state.error = .cantUpdateAnnotation } } @@ -346,7 +344,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro DDLogError("HtmlEpubReaderActionHandler: can't set tags \(key) - \(error)") - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in state.error = .cantUpdateAnnotation } } @@ -394,14 +392,14 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro private func set(filter: AnnotationsFilter?, in viewModel: ViewModel) { guard filter != viewModel.state.annotationFilter else { return } - self.filterAnnotations(with: viewModel.state.annotationSearchTerm, filter: filter, in: viewModel) + filterAnnotations(with: viewModel.state.annotationSearchTerm, filter: filter, in: viewModel) } private func searchAnnotations(for term: String, in viewModel: ViewModel) { let trimmedTerm = term.trimmingCharacters(in: .whitespacesAndNewlines) let newTerm = trimmedTerm.isEmpty ? nil : trimmedTerm guard newTerm != viewModel.state.annotationSearchTerm else { return } - self.filterAnnotations(with: newTerm, filter: viewModel.state.annotationFilter, in: viewModel) + filterAnnotations(with: newTerm, filter: viewModel.state.annotationFilter, in: viewModel) } /// Filters annotations based on given term and filer parameters. @@ -413,7 +411,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro // TODO: - Unhide document annotations - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in state.snapshotKeys = nil state.sortedKeys = snapshot state.changes = .annotations @@ -429,11 +427,11 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro } let snapshot = viewModel.state.snapshotKeys ?? viewModel.state.sortedKeys - let filteredKeys = self.filteredKeys(from: snapshot, term: term, filter: filter, state: viewModel.state) + let filteredKeys = filteredKeys(from: snapshot, term: term, filter: filter, state: viewModel.state) // TODO: - Hide document annotations - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in if state.snapshotKeys == nil { state.snapshotKeys = state.sortedKeys } @@ -503,12 +501,12 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro userId: viewModel.state.userId, schemaController: schemaController ) - self.perform(request: request) { [weak viewModel] error in - guard let error, let viewModel else { return } + perform(request: request) { [weak self, weak viewModel] error in + guard let self, let error, let viewModel else { return } DDLogError("HtmlEpubReaderActionHandler: could not store annotations - \(error)") - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in state.error = .cantAddAnnotations } } @@ -583,7 +581,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro } let documentData = HtmlEpubReaderState.DocumentData(type: type, buffer: jsArrayString, annotationsJson: json, page: page) - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in state.sortedKeys = sortedKeys state.annotations = annotations state.documentData = documentData @@ -598,9 +596,9 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro private func loadAnnotationsAndJson(in viewModel: ViewModel) -> ([String], [String: HtmlEpubAnnotation], String, NotificationToken?, String) { do { let pageIndexRequest = ReadDocumentDataDbRequest(attachmentKey: viewModel.state.key, libraryId: viewModel.state.library.identifier) - let pageIndex = try self.dbStorage.perform(request: pageIndexRequest, on: .main) + let pageIndex = try dbStorage.perform(request: pageIndexRequest, on: .main) let annotationsRequest = ReadAnnotationsDbRequest(attachmentKey: viewModel.state.key, libraryId: viewModel.state.library.identifier) - let items = try self.dbStorage.perform(request: annotationsRequest, on: .main) + let items = try dbStorage.perform(request: annotationsRequest, on: .main) var sortedKeys: [String] = [] var annotations: [String: HtmlEpubAnnotation] = [:] var jsons: [[String: Any]] = [] @@ -615,10 +613,11 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro let jsonString = WebViewEncoder.encodeAsJSONForJavascript(jsons) let token = items.observe { [weak self, weak viewModel] change in - guard let self = self, let viewModel = viewModel else { return } + guard let self, let viewModel else { return } switch change { case .update(let objects, let deletions, let insertions, let modifications): - self.update(objects: objects, deletions: deletions, insertions: insertions, modifications: modifications, viewModel: viewModel) + update(objects: objects, deletions: deletions, insertions: insertions, modifications: modifications, viewModel: viewModel) + case .error, .initial: break } } @@ -735,12 +734,12 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro } // Update state - self.update(viewModel: viewModel) { state in + update(viewModel: viewModel) { state in if state.snapshotKeys == nil { state.sortedKeys = keys } else { state.snapshotKeys = keys - state.sortedKeys = self.filteredKeys(from: keys, term: state.annotationSearchTerm, filter: state.annotationFilter, state: state) + state.sortedKeys = filteredKeys(from: keys, term: state.annotationSearchTerm, filter: state.annotationFilter, state: state) } state.annotations = annotations state.documentUpdate = HtmlEpubReaderState.DocumentUpdate(deletions: deletedPdfAnnotations, insertions: insertedPdfAnnotations, modifications: updatedPdfAnnotations) @@ -752,7 +751,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro // Update selection if selectionDeleted { - self._select(data: nil, didSelectInDocument: true, state: &state) + _select(data: nil, didSelectInDocument: true, state: &state) } // Disable sidebar editing if there are no results @@ -789,7 +788,7 @@ final class HtmlEpubReaderActionHandler: ViewModelActionHandler, BackgroundDbPro extension RItem { fileprivate var htmlEpubAnnotation: (HtmlEpubAnnotation, [String: Any])? { - let tags = Array(self.tags.map({ typedTag in + let tags = Array(tags.map({ typedTag in let color: String? = (typedTag.tag?.color ?? "").isEmpty ? nil : typedTag.tag?.color return Tag(name: typedTag.tag?.name ?? "", color: color ?? "") })) @@ -803,7 +802,7 @@ extension RItem { var color: String? var unknown: [String: String] = [:] - for field in self.fields { + for field in fields { switch (field.key, field.baseKey) { case (FieldKeys.Item.Annotation.Position.htmlEpubType, FieldKeys.Item.Annotation.position): position[FieldKeys.Item.Annotation.Position.htmlEpubType] = field.value @@ -843,10 +842,10 @@ extension RItem { } let json: [String: Any] = [ - "id": self.key, - "dateCreated": DateFormatter.iso8601WithFractionalSeconds.string(from: self.dateAdded), - "dateModified": DateFormatter.iso8601WithFractionalSeconds.string(from: self.dateModified), - "authorName": self.createdBy?.username ?? "", + "id": key, + "dateCreated": DateFormatter.iso8601WithFractionalSeconds.string(from: dateAdded), + "dateModified": DateFormatter.iso8601WithFractionalSeconds.string(from: dateModified), + "authorName": createdBy?.username ?? "", "type": type.rawValue, "text": text ?? "", "sortIndex": sortIndex, @@ -857,18 +856,18 @@ extension RItem { "tags": tags.map({ ["name": $0.name, "color": $0.color] }) ] let annotation = HtmlEpubAnnotation( - key: self.key, + key: key, type: type, pageLabel: pageLabel ?? "", position: position, - author: self.createdBy?.username ?? "", + author: createdBy?.username ?? "", isAuthor: true, color: color ?? "", comment: comment ?? "", text: text, sortIndex: sortIndex, - dateModified: self.dateModified, - dateCreated: self.dateAdded, + dateModified: dateModified, + dateCreated: dateAdded, tags: tags ) diff --git a/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubDocumentViewController.swift b/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubDocumentViewController.swift index c7b595a91..2dd353baf 100644 --- a/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubDocumentViewController.swift +++ b/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubDocumentViewController.swift @@ -27,7 +27,7 @@ class HtmlEpubDocumentViewController: UIViewController { init(viewModel: ViewModel) { self.viewModel = viewModel - self.disposeBag = DisposeBag() + disposeBag = DisposeBag() super.init(nibName: nil, bundle: nil) } @@ -36,8 +36,8 @@ class HtmlEpubDocumentViewController: UIViewController { } override func loadView() { - self.view = UIView() - self.view.backgroundColor = .systemBackground + view = UIView() + view.backgroundColor = .systemBackground } override func viewDidLoad() { @@ -50,8 +50,8 @@ class HtmlEpubDocumentViewController: UIViewController { func observeViewModel() { viewModel.stateObservable .observe(on: MainScheduler.instance) - .subscribe(with: self, onNext: { `self`, state in - self.process(state: state) + .subscribe(onNext: { [weak self] state in + self?.process(state: state) }) .disposed(by: disposeBag) } @@ -59,17 +59,17 @@ class HtmlEpubDocumentViewController: UIViewController { func setupWebView() { let webView = WKWebView() webView.translatesAutoresizingMaskIntoConstraints = false - self.view.addSubview(webView) + view.addSubview(webView) NSLayoutConstraint.activate([ - self.view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: webView.topAnchor), - self.view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: webView.bottomAnchor), - self.view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: webView.leadingAnchor), - self.view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: webView.trailingAnchor) + view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: webView.topAnchor), + view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: webView.bottomAnchor), + view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: webView.leadingAnchor), + view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: webView.trailingAnchor) ]) self.webView = webView - self.webViewHandler = WebViewHandler(webView: webView, javascriptHandlers: JSHandlers.allCases.map({ $0.rawValue })) - self.webViewHandler.receivedMessageHandler = { [weak self] handler, message in + webViewHandler = WebViewHandler(webView: webView, javascriptHandlers: JSHandlers.allCases.map({ $0.rawValue })) + webViewHandler.receivedMessageHandler = { [weak self] handler, message in self?.process(handler: handler, message: message) } } @@ -79,9 +79,7 @@ class HtmlEpubDocumentViewController: UIViewController { DDLogError("HtmlEpubReaderViewController: can't load reader view.html") return } - self.webViewHandler.load(fileUrl: readerUrl) - .subscribe() - .disposed(by: self.disposeBag) + webViewHandler.load(fileUrl: readerUrl).subscribe().disposed(by: disposeBag) } } @@ -118,9 +116,7 @@ class HtmlEpubDocumentViewController: UIViewController { func set(tool data: (AnnotationTool, UIColor)?) { guard let (tool, color) = data else { - self.webViewHandler.call(javascript: "clearTool();") - .subscribe() - .disposed(by: self.disposeBag) + webViewHandler.call(javascript: "clearTool();").subscribe().disposed(by: disposeBag) return } @@ -135,28 +131,25 @@ class HtmlEpubDocumentViewController: UIViewController { case .eraser, .image, .ink: return } - - self.webViewHandler.call(javascript: "setTool({ type: '\(toolName)', color: '\(color.hexString)' });") - .subscribe() - .disposed(by: self.disposeBag) + webViewHandler.call(javascript: "setTool({ type: '\(toolName)', color: '\(color.hexString)' });").subscribe().disposed(by: disposeBag) } func search(term: String) { webViewHandler.call(javascript: "search({ term: \(WebViewEncoder.encodeForJavascript(term.data(using: .utf8))) });") .observe(on: MainScheduler.instance) - .subscribe(with: self, onFailure: { _, error in + .subscribe(onFailure: { error in DDLogError("HtmlEpubReaderViewController: searching document failed - \(error)") }) - .disposed(by: self.disposeBag) + .disposed(by: disposeBag) } func selectInDocument(key: String) { webViewHandler.call(javascript: "select({ key: '\(key)' });") .observe(on: MainScheduler.instance) - .subscribe(with: self, onFailure: { _, error in + .subscribe(onFailure: { error in DDLogError("HtmlEpubReaderViewController: navigating to \(key) failed - \(error)") }) - .disposed(by: self.disposeBag) + .disposed(by: disposeBag) } func updateView(modifications: [[String: Any]], insertions: [[String: Any]], deletions: [String]) { @@ -165,10 +158,10 @@ class HtmlEpubDocumentViewController: UIViewController { let encodedModifications = WebViewEncoder.encodeAsJSONForJavascript(modifications) webViewHandler.call(javascript: "updateAnnotations({ deletions: \(encodedDeletions), insertions: \(encodedInsertions), modifications: \(encodedModifications)});") .observe(on: MainScheduler.instance) - .subscribe(with: self, onFailure: { _, error in + .subscribe(onFailure: { error in DDLogError("HtmlEpubReaderViewController: updating document failed - \(error)") }) - .disposed(by: self.disposeBag) + .disposed(by: disposeBag) } func load(documentData data: HtmlEpubReaderState.DocumentData) { @@ -187,10 +180,10 @@ class HtmlEpubDocumentViewController: UIViewController { webViewHandler.call(javascript: javascript) .observe(on: MainScheduler.instance) - .subscribe(with: self, onFailure: { _, error in + .subscribe(onFailure: { error in DDLogError("HtmlEpubReaderViewController: loading document failed - \(error)") }) - .disposed(by: self.disposeBag) + .disposed(by: disposeBag) } } @@ -209,7 +202,7 @@ class HtmlEpubDocumentViewController: UIViewController { switch event { case "onInitialized": - self.viewModel.process(action: .loadDocument) + viewModel.process(action: .loadDocument) case "onSaveAnnotations": guard let params = data["params"] as? [String: Any] else { @@ -217,7 +210,7 @@ class HtmlEpubDocumentViewController: UIViewController { return } DDLogInfo("HtmlEpubReaderViewController: \(params)") - self.viewModel.process(action: .saveAnnotations(params)) + viewModel.process(action: .saveAnnotations(params)) case "onSetAnnotationPopup": guard let params = data["params"] as? [String: Any] else { @@ -226,7 +219,7 @@ class HtmlEpubDocumentViewController: UIViewController { } if params.isEmpty { - self.viewModel.process(action: .deselectSelectedAnnotation) + viewModel.process(action: .deselectSelectedAnnotation) return } @@ -235,16 +228,16 @@ class HtmlEpubDocumentViewController: UIViewController { return } - let navigationBarInset = (self.parentDelegate?.statusBarHeight ?? 0) + (self.parentDelegate?.navigationBarHeight ?? 0) + let navigationBarInset = (parentDelegate?.statusBarHeight ?? 0) + (parentDelegate?.navigationBarHeight ?? 0) let rect = CGRect(x: rectArray[0], y: rectArray[1] + navigationBarInset, width: rectArray[2] - rectArray[0], height: rectArray[3] - rectArray[1]) - self.viewModel.process(action: .selectAnnotationFromDocument(key: key, rect: rect)) + viewModel.process(action: .selectAnnotationFromDocument(key: key, rect: rect)) case "onChangeViewState": guard let params = data["params"] as? [String: Any] else { DDLogWarn("HtmlEpubReaderViewController: event \(event) missing params - \(message)") return } - self.viewModel.process(action: .setViewState(params)) + viewModel.process(action: .setViewState(params)) default: break diff --git a/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubReaderViewController.swift b/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubReaderViewController.swift index b5b2996fd..9e8c2a47d 100644 --- a/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubReaderViewController.swift +++ b/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubReaderViewController.swift @@ -64,9 +64,9 @@ class HtmlEpubReaderViewController: UIViewController { checkbox.isSelected = toolbarState.visible checkbox.rx.controlEvent(.touchUpInside) .subscribe(onNext: { [weak self, weak checkbox] _ in - guard let self = self, let checkbox = checkbox else { return } + guard let self, let checkbox else { return } checkbox.isSelected = !checkbox.isSelected - self.annotationToolbarHandler.set(hidden: !checkbox.isSelected, animated: true) + annotationToolbarHandler.set(hidden: !checkbox.isSelected, animated: true) }) .disposed(by: disposeBag) let barButton = UIBarButtonItem(customView: checkbox) @@ -82,7 +82,7 @@ class HtmlEpubReaderViewController: UIViewController { settings.rx.tap .subscribe(onNext: { [weak self, weak settings] _ in guard let self, let settings else { return } - self.showSettings(sender: settings) + showSettings(sender: settings) }) .disposed(by: disposeBag) return settings @@ -92,9 +92,9 @@ class HtmlEpubReaderViewController: UIViewController { init(viewModel: ViewModel, compactSize: Bool) { self.viewModel = viewModel - self.isCompactWidth = compactSize - self.disposeBag = DisposeBag() - self.statusBarHeight = UIApplication + isCompactWidth = compactSize + disposeBag = DisposeBag() + statusBarHeight = UIApplication .shared .connectedScenes .filter({ $0.activationState == .foregroundActive }) @@ -113,14 +113,10 @@ class HtmlEpubReaderViewController: UIViewController { fatalError("init(coder:) has not been implemented") } - override func loadView() { - self.view = UIView() - self.view.backgroundColor = .systemBackground - } - override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemBackground observeViewModel() setupNavigationBar() setupSearch() @@ -131,8 +127,8 @@ class HtmlEpubReaderViewController: UIViewController { func observeViewModel() { viewModel.stateObservable .observe(on: MainScheduler.instance) - .subscribe(with: self, onNext: { `self`, state in - self.process(state: state) + .subscribe(onNext: { [weak self] state in + self?.process(state: state) }) .disposed(by: disposeBag) } @@ -141,12 +137,12 @@ class HtmlEpubReaderViewController: UIViewController { let closeButton = UIBarButtonItem(image: UIImage(systemName: "chevron.left"), style: .plain, target: nil, action: nil) closeButton.title = L10n.close closeButton.accessibilityLabel = L10n.close - closeButton.rx.tap.subscribe(with: self, onNext: { _, _ in self.close() }).disposed(by: disposeBag) + closeButton.rx.tap.subscribe(onNext: { [weak self] _ in self?.close() }).disposed(by: disposeBag) let sidebarButton = UIBarButtonItem(image: UIImage(systemName: "sidebar.left"), style: .plain, target: nil, action: nil) setupAccessibility(forSidebarButton: sidebarButton) sidebarButton.tag = Self.sidebarButtonTag - sidebarButton.rx.tap.subscribe(with: self, onNext: { `self`, _ in self.toggleSidebar(animated: true) }).disposed(by: disposeBag) + sidebarButton.rx.tap.subscribe(onNext: { [weak self] _ in self?.toggleSidebar(animated: true) }).disposed(by: disposeBag) navigationItem.leftBarButtonItems = [closeButton, sidebarButton] } @@ -227,12 +223,12 @@ class HtmlEpubReaderViewController: UIViewController { self.documentController = documentController self.sidebarController = sidebarController - self.annotationToolbarController = annotationToolbar - self.documentTop = documentTopConstraint - self.documentLeft = documentLeftConstraint - self.sidebarLeft = sidebarLeftConstraint - self.annotationToolbarHandler = AnnotationToolbarHandler(controller: annotationToolbar, delegate: self) - self.annotationToolbarHandler.performInitialLayout() + annotationToolbarController = annotationToolbar + documentTop = documentTopConstraint + documentLeft = documentLeftConstraint + sidebarLeft = sidebarLeftConstraint + annotationToolbarHandler = AnnotationToolbarHandler(controller: annotationToolbar, delegate: self) + annotationToolbarHandler.performInitialLayout() func add(controller: UIViewController) { controller.willMove(toParent: self) @@ -266,9 +262,10 @@ class HtmlEpubReaderViewController: UIViewController { guard viewIfLoaded != nil else { return } - coordinator.animate(alongsideTransition: { _ in - self.statusBarHeight = self.view.safeAreaInsets.top - (self.navigationController?.isNavigationBarHidden == true ? 0 : self.navigationBarHeight) - self.annotationToolbarHandler.viewWillTransitionToNewSize() + coordinator.animate(alongsideTransition: { [weak self] _ in + guard let self else { return } + statusBarHeight = view.safeAreaInsets.top - (navigationController?.isNavigationBarHidden == true ? 0 : navigationBarHeight) + annotationToolbarHandler.viewWillTransitionToNewSize() }, completion: nil) } @@ -322,21 +319,22 @@ class HtmlEpubReaderViewController: UIViewController { func observe(key: String, popoverObservable observable: PublishSubject?) { guard let observable else { return } - observable.subscribe(with: self) { `self`, state in + observable.subscribe { [weak self] state in + guard let self else { return } if state.changes.contains(.color) { - self.viewModel.process(action: .setColor(key: key, color: state.color)) + viewModel.process(action: .setColor(key: key, color: state.color)) } if state.changes.contains(.comment) { - self.viewModel.process(action: .setComment(key: key, comment: state.comment)) + viewModel.process(action: .setComment(key: key, comment: state.comment)) } if state.changes.contains(.deletion) { - self.viewModel.process(action: .removeAnnotation(key)) + viewModel.process(action: .removeAnnotation(key)) } if state.changes.contains(.tags) { - self.viewModel.process(action: .setTags(key: key, tags: state.tags)) + viewModel.process(action: .setTags(key: key, tags: state.tags)) } if state.changes.contains(.pageLabel) || state.changes.contains(.highlight) { - self.viewModel.process(action: + viewModel.process(action: .updateAnnotationProperties( key: key, color: state.color, @@ -358,9 +356,9 @@ class HtmlEpubReaderViewController: UIViewController { guard let viewModel = coordinatorDelegate?.showSettings(with: viewModel.state.settings, sender: sender) else { return } viewModel.stateObservable .observe(on: MainScheduler.instance) - .subscribe(with: self, onNext: { `self`, state in + .subscribe(onNext: { [weak self] state in let settings = HtmlEpubSettings(appearance: state.appearance, idleTimerDisabled: state.idleTimerDisabled) - self.viewModel.process(action: .setSettings(settings)) + self?.viewModel.process(action: .setSettings(settings)) }) .disposed(by: disposeBag) } @@ -410,18 +408,23 @@ class HtmlEpubReaderViewController: UIViewController { view.endEditing(true) } - UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 5, options: [.curveEaseOut], - animations: { - self.annotationToolbarController.prepareForSizeChange() - self.view.layoutIfNeeded() - self.annotationToolbarController.sizeDidChange() - }, - completion: { finished in - guard finished else { return } - if !shouldShow { - self.sidebarController.view.isHidden = true - } - }) + UIView.animate( + withDuration: 0.3, + delay: 0, + usingSpringWithDamping: 1, + initialSpringVelocity: 5, + options: [.curveEaseOut], + animations: { + self.annotationToolbarController.prepareForSizeChange() + self.view.layoutIfNeeded() + self.annotationToolbarController.sizeDidChange() + }, + completion: { finished in + guard finished else { return } + if !shouldShow { + self.sidebarController.view.isHidden = true + } + }) } private func setupAccessibility(forSidebarButton button: UIBarButtonItem) { diff --git a/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubSidebarViewController.swift b/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubSidebarViewController.swift index ec2f940cf..02f18d32e 100644 --- a/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubSidebarViewController.swift +++ b/Zotero/Scenes/Detail/HTML:EPUB/Views/HtmlEpubSidebarViewController.swift @@ -26,8 +26,7 @@ class HtmlEpubSidebarViewController: UIViewController { init(viewModel: ViewModel) { self.viewModel = viewModel - self.disposeBag = DisposeBag() - + disposeBag = DisposeBag() super.init(nibName: nil, bundle: nil) } @@ -38,7 +37,7 @@ class HtmlEpubSidebarViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - self.view.backgroundColor = .systemGray6 + view.backgroundColor = .systemGray6 setupViews() setupSearchController() setupDataSource() @@ -53,8 +52,8 @@ class HtmlEpubSidebarViewController: UIViewController { func setupObserving() { viewModel.stateObservable .observe(on: MainScheduler.instance) - .subscribe(with: self, onNext: { `self`, state in - self.update(state: state) + .subscribe(onNext: { [weak self] state in + self?.update(state: state) }) .disposed(by: disposeBag) } @@ -74,8 +73,8 @@ class HtmlEpubSidebarViewController: UIViewController { searchBar.text .observe(on: MainScheduler.instance) .debounce(.milliseconds(150), scheduler: MainScheduler.instance) - .subscribe(with: self, onNext: { `self`, text in - self.viewModel.process(action: .searchAnnotations(text)) + .subscribe(onNext: { [weak self] text in + self?.viewModel.process(action: .searchAnnotations(text)) }) .disposed(by: disposeBag) tableView.tableHeaderView = searchBar @@ -90,13 +89,13 @@ class HtmlEpubSidebarViewController: UIViewController { tableView.backgroundView?.backgroundColor = .systemGray6 tableView.register(AnnotationCell.self, forCellReuseIdentifier: Self.cellId) tableView.allowsMultipleSelectionDuringEditing = true - self.view.addSubview(tableView) + view.addSubview(tableView) self.tableView = tableView let toolbarContainer = UIView() toolbarContainer.isHidden = !viewModel.state.library.metadataEditable toolbarContainer.translatesAutoresizingMaskIntoConstraints = false - self.view.addSubview(toolbarContainer) + view.addSubview(toolbarContainer) self.toolbarContainer = toolbarContainer let toolbar = UIToolbar() @@ -106,27 +105,27 @@ class HtmlEpubSidebarViewController: UIViewController { self.toolbar = toolbar NSLayoutConstraint.activate([ - self.view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: tableView.topAnchor), + view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: tableView.topAnchor), tableView.bottomAnchor.constraint(equalTo: toolbarContainer.topAnchor), - self.view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: tableView.leadingAnchor), - self.view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: tableView.trailingAnchor), - toolbarContainer.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), - toolbarContainer.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), - toolbarContainer.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), + view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: tableView.leadingAnchor), + view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: tableView.trailingAnchor), + toolbarContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor), + toolbarContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor), + toolbarContainer.bottomAnchor.constraint(equalTo: view.bottomAnchor), toolbar.topAnchor.constraint(equalTo: toolbarContainer.topAnchor), toolbar.leadingAnchor.constraint(equalTo: toolbarContainer.leadingAnchor), toolbar.trailingAnchor.constraint(equalTo: toolbarContainer.trailingAnchor), - toolbar.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor) + toolbar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) } func setupDataSource() { - dataSource = TableViewDiffableDataSource(tableView: self.tableView, cellProvider: { [weak self] tableView, indexPath, key in + dataSource = TableViewDiffableDataSource(tableView: tableView, cellProvider: { [weak self] tableView, indexPath, key in let cell = tableView.dequeueReusableCell(withIdentifier: Self.cellId, for: indexPath) - if let self, let cell = cell as? AnnotationCell, let annotation = self.viewModel.state.annotations[key] { + if let self, let cell = cell as? AnnotationCell, let annotation = viewModel.state.annotations[key] { cell.contentView.backgroundColor = self.view.backgroundColor - setup(cell: cell, with: annotation, state: self.viewModel.state) + setup(cell: cell, with: annotation, state: viewModel.state) } return cell @@ -136,8 +135,8 @@ class HtmlEpubSidebarViewController: UIViewController { return true } dataSource.commitEditingStyle = { [weak self] editingStyle, indexPath in - guard let self, editingStyle == .delete, let key = self.dataSource.itemIdentifier(for: indexPath) else { return } - self.viewModel.process(action: .removeAnnotation(key)) + guard let self, editingStyle == .delete, let key = dataSource.itemIdentifier(for: indexPath) else { return } + viewModel.process(action: .removeAnnotation(key)) } } @@ -166,7 +165,7 @@ class HtmlEpubSidebarViewController: UIViewController { guard !comment.isEmpty else { return nil } - if let attributedComment = self.viewModel.state.comments[annotation.key] { + if let attributedComment = viewModel.state.comments[annotation.key] { return attributedComment } @@ -204,14 +203,11 @@ class HtmlEpubSidebarViewController: UIViewController { if let keys = state.updatedAnnotationKeys { snapshot.reloadItems(keys) } - let isVisible = parentDelegate?.isSidebarVisible ?? false - if state.changes.contains(.sidebarEditing) { tableView.setEditing(state.sidebarEditingEnabled, animated: isVisible) } dataSource.apply(snapshot, animatingDifferences: isVisible, completion: completion) - return } @@ -259,9 +255,14 @@ class HtmlEpubSidebarViewController: UIViewController { case .tags: guard annotation.isAuthor else { return } let selected = Set(annotation.tags.map({ $0.name })) - coordinatorDelegate?.showTagPicker(libraryId: state.library.identifier, selected: selected, userInterfaceStyle: viewModel.state.settings.appearance.userInterfaceStyle, picked: { [weak self] tags in - self?.viewModel.process(action: .setTags(key: annotation.key, tags: tags)) - }) + coordinatorDelegate?.showTagPicker( + libraryId: state.library.identifier, + selected: selected, + userInterfaceStyle: viewModel.state.settings.appearance.userInterfaceStyle, + picked: { [weak self] tags in + self?.viewModel.process(action: .setTags(key: annotation.key, tags: tags)) + } + ) case .options(let sender): guard let sender else { return } @@ -322,7 +323,7 @@ class HtmlEpubSidebarViewController: UIViewController { let safeAreaTop = tableView.superview!.safeAreaInsets.top // Scroll either when cell bottom is below keyboard or cell top is not visible on screen - if cellBottom > tableViewBottom || cellFrame.minY < (safeAreaTop + self.tableView.contentOffset.y) { + if cellBottom > tableViewBottom || cellFrame.minY < (safeAreaTop + tableView.contentOffset.y) { // Scroll to top if cell is smaller than visible screen, so that it's fully visible, otherwise scroll to bottom. let position: UITableView.ScrollPosition = cellFrame.height + safeAreaTop < tableViewBottom ? .top : .bottom tableView.scrollToRow(at: indexPath, at: position, animated: false) @@ -339,9 +340,9 @@ class HtmlEpubSidebarViewController: UIViewController { let delete = UIBarButtonItem(title: L10n.delete, style: .plain, target: nil, action: nil) delete.isEnabled = deletionEnabled delete.rx.tap - .subscribe(with: self, onNext: { `self`, _ in - guard self.viewModel.state.sidebarEditingEnabled else { return } - self.viewModel.process(action: .removeSelectedAnnotations) + .subscribe(onNext: { [weak self] _ in + guard let self, viewModel.state.sidebarEditingEnabled else { return } + viewModel.process(action: .removeSelectedAnnotations) }) .disposed(by: disposeBag) items.append(delete) @@ -352,9 +353,9 @@ class HtmlEpubSidebarViewController: UIViewController { let filterImageName = filterOn ? "line.horizontal.3.decrease.circle.fill" : "line.horizontal.3.decrease.circle" let filter = UIBarButtonItem(image: UIImage(systemName: filterImageName), style: .plain, target: nil, action: nil) filter.rx.tap - .subscribe(with: self, onNext: { [weak filter] `self`, _ in - guard let filter else { return } - showFilterPopup(from: filter, viewModel: self.viewModel, coordinatorDelegate: self.coordinatorDelegate) + .subscribe(onNext: { [weak self, weak filter] _ in + guard let self, let filter else { return } + showFilterPopup(from: filter, viewModel: viewModel, coordinatorDelegate: coordinatorDelegate) }) .disposed(by: disposeBag) items.insert(filter, at: 0) @@ -362,8 +363,8 @@ class HtmlEpubSidebarViewController: UIViewController { let select = UIBarButtonItem(title: (editingEnabled ? L10n.done : L10n.select), style: .plain, target: nil, action: nil) select.rx.tap - .subscribe(with: self, onNext: { `self`, _ in - self.viewModel.process(action: .setSidebarEditingEnabled(!editingEnabled)) + .subscribe(onNext: { [weak self] _ in + self?.viewModel.process(action: .setSidebarEditingEnabled(!editingEnabled)) }) .disposed(by: disposeBag) items.append(select) diff --git a/Zotero/Scenes/Detail/PDF/Views/Annotation View/AnnotationView.swift b/Zotero/Scenes/Detail/PDF/Views/Annotation View/AnnotationView.swift index b06154e39..ba4f8eac5 100644 --- a/Zotero/Scenes/Detail/PDF/Views/Annotation View/AnnotationView.swift +++ b/Zotero/Scenes/Detail/PDF/Views/Annotation View/AnnotationView.swift @@ -95,7 +95,7 @@ final class AnnotationView: UIView { let canEdit = library.metadataEditable && selected let author = library.identifier == .custom(.myLibrary) ? "" : annotation.author - self.header.setup( + header.setup( type: annotation.type, authorName: author, pageLabel: annotation.pageLabel, @@ -107,7 +107,7 @@ final class AnnotationView: UIView { showsLock: !library.metadataEditable, accessibilityType: .cell ) - self.setupContent( + setupContent( type: annotation.type, comment: annotation.comment, text: annotation.text, @@ -117,18 +117,18 @@ final class AnnotationView: UIView { availableWidth: availableWidth, accessibilityType: .cell ) - self.setup(comment: comment, canEdit: canEdit) - self.setup(tags: annotation.tags, canEdit: canEdit, accessibilityEnabled: selected) - self.setupObserving() + setup(comment: comment, canEdit: canEdit) + setup(tags: annotation.tags, canEdit: canEdit, accessibilityEnabled: selected) + setupObserving() - let commentButtonIsHidden = self.commentTextView.isHidden - let highlightContentIsHidden = self.highlightContent?.isHidden ?? true - let imageContentIsHidden = self.imageContent?.isHidden ?? true + let commentButtonIsHidden = commentTextView.isHidden + let highlightContentIsHidden = highlightContent?.isHidden ?? true + let imageContentIsHidden = imageContent?.isHidden ?? true // Top separator is hidden only if there is only header visible and nothing else - self.topSeparator.isHidden = self.commentTextView.isHidden && commentButtonIsHidden && highlightContentIsHidden && imageContentIsHidden && self.tags.isHidden && self.tagsButton.isHidden + topSeparator.isHidden = commentTextView.isHidden && commentButtonIsHidden && highlightContentIsHidden && imageContentIsHidden && tags.isHidden && tagsButton.isHidden // Bottom separator is visible, when tags are showing (either actual tags or tags button) and there is something visible above them (other than header, either content or comments/comments button) - self.bottomSeparator.isHidden = (self.tags.isHidden && self.tagsButton.isHidden) || (self.commentTextView.isHidden && commentButtonIsHidden && highlightContentIsHidden && imageContentIsHidden) + bottomSeparator.isHidden = (tags.isHidden && tagsButton.isHidden) || (commentTextView.isHidden && commentButtonIsHidden && highlightContentIsHidden && imageContentIsHidden) } /// Setups up annotation view with given annotation and additional data. diff --git a/Zotero/Scenes/Detail/PDF/Views/AnnotationCell.swift b/Zotero/Scenes/Detail/PDF/Views/AnnotationCell.swift index 957d7ff18..89e3c9177 100644 --- a/Zotero/Scenes/Detail/PDF/Views/AnnotationCell.swift +++ b/Zotero/Scenes/Detail/PDF/Views/AnnotationCell.swift @@ -100,7 +100,7 @@ final class AnnotationCell: UITableViewCell { key = annotation.key selectionView.layer.borderWidth = selected ? PDFReaderLayout.cellSelectionLineWidth : 0 let availableWidth = availableWidth - (PDFReaderLayout.annotationLayout.horizontalInset * 2) - self.annotationView.setup( + annotationView.setup( with: annotation, comment: comment, selected: selected, @@ -109,7 +109,7 @@ final class AnnotationCell: UITableViewCell { currentUserId: currentUserId ) - self.setupAccessibility( + setupAccessibility( isAuthor: annotation.isAuthor, authorName: annotation.author, type: annotation.type, @@ -136,13 +136,13 @@ final class AnnotationCell: UITableViewCell { state: PDFReaderState ) { if !selected { - self.annotationView.resignFirstResponder() + annotationView.resignFirstResponder() } - self.key = annotation.key - self.selectionView.layer.borderWidth = selected ? PDFReaderLayout.cellSelectionLineWidth : 0 + key = annotation.key + selectionView.layer.borderWidth = selected ? PDFReaderLayout.cellSelectionLineWidth : 0 let availableWidth = availableWidth - (PDFReaderLayout.annotationLayout.horizontalInset * 2) - self.annotationView.setup( + annotationView.setup( with: annotation, comment: comment, preview: preview, @@ -157,7 +157,7 @@ final class AnnotationCell: UITableViewCell { state: state ) - self.setupAccessibility( + setupAccessibility( isAuthor: annotation.isAuthor(currentUserId: currentUserId), authorName: annotation.author(displayName: displayName, username: username), type: annotation.type, @@ -170,7 +170,7 @@ final class AnnotationCell: UITableViewCell { private func setupAccessibility(isAuthor: Bool, authorName: String, type: AnnotationType, pageLabel: String, text: String?, comment: String, selected: Bool) { let author = isAuthor ? nil : authorName - var label = self.accessibilityLabel(for: type, pageLabel: pageLabel, author: author) + var label = accessibilityLabel(for: type, pageLabel: pageLabel, author: author) if let text { label += ", " + L10n.Accessibility.Pdf.highlightedText + ": " + text } diff --git a/Zotero/Scenes/General/Models/ReaderSettingsState.swift b/Zotero/Scenes/General/Models/ReaderSettingsState.swift index 2a7213341..0f2dfb5b6 100644 --- a/Zotero/Scenes/General/Models/ReaderSettingsState.swift +++ b/Zotero/Scenes/General/Models/ReaderSettingsState.swift @@ -33,22 +33,22 @@ struct ReaderSettingsState: ViewModelState { var idleTimerDisabled: Bool init(settings: PDFSettings) { - self.transition = settings.transition - self.pageMode = settings.pageMode - self.scrollDirection = settings.direction - self.pageFitting = settings.pageFitting - self.appearance = settings.appearanceMode - self.idleTimerDisabled = settings.idleTimerDisabled + transition = settings.transition + pageMode = settings.pageMode + scrollDirection = settings.direction + pageFitting = settings.pageFitting + appearance = settings.appearanceMode + idleTimerDisabled = settings.idleTimerDisabled } init(settings: HtmlEpubSettings) { - self.appearance = settings.appearance - self.idleTimerDisabled = settings.idleTimerDisabled + appearance = settings.appearance + idleTimerDisabled = settings.idleTimerDisabled // These don't apply to HTML/Epub, assign random values - self.transition = .curl - self.pageMode = .automatic - self.scrollDirection = .horizontal - self.pageFitting = .adaptive + transition = .curl + pageMode = .automatic + scrollDirection = .horizontal + pageFitting = .adaptive } func cleanup() {}