Skip to content

Commit

Permalink
Implemented settings, bug fixes & improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
michalrentka committed Nov 30, 2023
1 parent 1faeec3 commit d2fe8db
Show file tree
Hide file tree
Showing 38 changed files with 555 additions and 377 deletions.
10 changes: 5 additions & 5 deletions ZShare/Controllers/TranslationWebViewHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ final class TranslationWebViewHandler {
}
.flatMap { translators -> Single<Any> in
DDLogInfo("WebViewHandler: encode translators")
let encodedTranslators = WKWebView.encodeAsJSONForJavascript(translators)
let encodedTranslators = WebViewEncoder.encodeAsJSONForJavascript(translators)
return self.webViewHandler.call(javascript: "initTranslators(\(encodedTranslators));")
}
.flatMap({ _ -> Single<Any> in
DDLogInfo("WebViewHandler: call translate js")
let encodedHtml = WKWebView.encodeForJavascript(html.data(using: .utf8))
let encodedHtml = WebViewEncoder.encodeForJavascript(html.data(using: .utf8))
let jsonFramesData = try? JSONSerialization.data(withJSONObject: frames, options: .fragmentsAllowed)
let encodedFrames = jsonFramesData.flatMap({ WKWebView.encodeForJavascript($0) }) ?? "''"
let encodedFrames = jsonFramesData.flatMap({ WebViewEncoder.encodeForJavascript($0) }) ?? "''"
return self.webViewHandler.call(javascript: "translate('\(url.absoluteString)', \(encodedHtml), \(encodedFrames));")
})
.subscribe(onFailure: { [weak self] error in
Expand All @@ -169,8 +169,8 @@ final class TranslationWebViewHandler {
return Disposables.create()
}

let encodedSchema = WKWebView.encodeForJavascript(schemaData)
let encodedFormats = WKWebView.encodeForJavascript(dateFormatData)
let encodedSchema = WebViewEncoder.encodeForJavascript(schemaData)
let encodedFormats = WebViewEncoder.encodeForJavascript(dateFormatData)

DDLogInfo("WebViewHandler: loaded bundled files")

Expand Down
64 changes: 35 additions & 29 deletions Zotero.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Zotero/Assets/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@
"errors.pdf.cant_update_annotation" = "Can't update annotation.";
"errors.pdf.cant_add_annotations" = "Can't add annotations.";
"errors.pdf.cant_delete_annotations" = "Can't delete annotations.";
"errors.pdf.incompatible_document" = "This document is not supported.";
"errors.pdf.page_index_not_int" = "Incorrect format of page stored for this document.";

"accessibility.untitled" = "Untitled";
Expand Down
10 changes: 5 additions & 5 deletions Zotero/Controllers/Citation/CitationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class CitationController: NSObject {
}
itemsData.append(data)
}
return WKWebView.encodeAsJSONForJavascript(itemsData)
return WebViewEncoder.encodeAsJSONForJavascript(itemsData)
}

/// Bibliography happens once for selected item(s). Appropriate style and locale xmls are loaded, webView is initialized and loaded with index.html. When everything is loaded,
Expand Down Expand Up @@ -266,7 +266,7 @@ class CitationController: NSObject {
let localeData = try Data(contentsOf: localeUrl)
let styleData = try self.fileStorage.read(Files.style(filename: styleFilename))

subscriber(.success((WKWebView.encodeForJavascript(styleData), WKWebView.encodeForJavascript(localeData))))
subscriber(.success((WebViewEncoder.encodeForJavascript(styleData), WebViewEncoder.encodeForJavascript(localeData))))
} catch let error {
DDLogError("CitationController: can't read locale or style - \(error)")
subscriber(.failure(Error.styleOrLocaleMissing))
Expand Down Expand Up @@ -302,7 +302,7 @@ class CitationController: NSObject {
return Disposables.create()
}

subscriber(.success(WKWebView.encodeForJavascript(schemaData)))
subscriber(.success(WebViewEncoder.encodeForJavascript(schemaData)))

return Disposables.create()
}
Expand All @@ -323,7 +323,7 @@ class CitationController: NSObject {

items.first?.realm?.invalidate()

subscriber(.success(WKWebView.encodeAsJSONForJavascript(data)))
subscriber(.success(WebViewEncoder.encodeAsJSONForJavascript(data)))
} catch let error {
DDLogError("CitationController: can't read items - \(error)")
subscriber(.failure(error))
Expand Down Expand Up @@ -498,7 +498,7 @@ extension CitationController: WKScriptMessageHandler {

case .csl:
if let csl = jsResult as? [[String: Any]] {
result = .success(WKWebView.encodeAsJSONForJavascript(csl))
result = .success(WebViewEncoder.encodeAsJSONForJavascript(csl))
} else {
DDLogError("CitationController: CSL got unknown response - \(jsResult)")
result = .failure(Error.missingResponse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ final class LookupWebViewHandler {

private func _lookUp(identifier: String) {
DDLogInfo("LookupWebViewHandler: call translate js")
let encodedIdentifiers = WKWebView.encodeForJavascript(identifier.data(using: .utf8))
let encodedIdentifiers = WebViewEncoder.encodeForJavascript(identifier.data(using: .utf8))
return self.webViewHandler.call(javascript: "lookup(\(encodedIdentifiers));")
.subscribe(on: MainScheduler.instance)
.observe(on: MainScheduler.instance)
Expand Down Expand Up @@ -151,7 +151,7 @@ final class LookupWebViewHandler {
}
.flatMap { translators -> Single<Any> in
DDLogInfo("LookupWebViewHandler: encode translators")
let encodedTranslators = WKWebView.encodeAsJSONForJavascript(translators)
let encodedTranslators = WebViewEncoder.encodeAsJSONForJavascript(translators)
return self.webViewHandler.call(javascript: "initTranslators(\(encodedTranslators));")
}
}
Expand Down Expand Up @@ -179,8 +179,8 @@ final class LookupWebViewHandler {
return Disposables.create()
}

let encodedSchema = WKWebView.encodeForJavascript(schemaData)
let encodedFormats = WKWebView.encodeForJavascript(dateFormatData)
let encodedSchema = WebViewEncoder.encodeForJavascript(schemaData)
let encodedFormats = WebViewEncoder.encodeForJavascript(dateFormatData)

DDLogInfo("WebViewHandler: loaded bundled files")

Expand Down
2 changes: 1 addition & 1 deletion Zotero/Controllers/Web View Handling/WebViewHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ final class WebViewHandler: NSObject {
}

func sendMessaging(response payload: [String: Any], for messageId: Int) {
let script = "Zotero.Messaging.receiveResponse('\(messageId)', \(WKWebView.encodeAsJSONForJavascript(payload)));"
let script = "Zotero.Messaging.receiveResponse('\(messageId)', \(WebViewEncoder.encodeAsJSONForJavascript(payload)));"
inMainThread { [weak self] in
self?.webView?.evaluateJavaScript(script, completionHandler: nil)
}
Expand Down
26 changes: 26 additions & 0 deletions Zotero/Controllers/WebViewEncoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// WebViewEncoder.swift
// Zotero
//
// Created by Michal Rentka on 15.11.2023.
// Copyright © 2023 Corporation for Digital Scholarship. All rights reserved.
//

import Foundation

struct WebViewEncoder {
static func optionalToJs(_ value: String?) -> String {
return value.flatMap({ "'" + $0 + "'" }) ?? "null"
}

/// Encodes data which need to be sent to `webView`. All data that is passed to JS is Base64 encoded so that it can be sent as a simple `String`.
static func encodeForJavascript(_ data: Data?) -> String {
return data.flatMap({ "'" + $0.base64EncodedString(options: .endLineWithLineFeed) + "'" }) ?? "null"
}

/// Encodes as JSON payload so that it can be sent to `webView`.
static func encodeAsJSONForJavascript(_ payload: Any) -> String {
let data = try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted)
return self.encodeForJavascript(data)
}
}
2 changes: 2 additions & 0 deletions Zotero/Extensions/Localizable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,8 @@ internal enum L10n {
internal static let cantDeleteAnnotations = L10n.tr("Localizable", "errors.pdf.cant_delete_annotations", fallback: "Can't delete annotations.")
/// Can't update annotation.
internal static let cantUpdateAnnotation = L10n.tr("Localizable", "errors.pdf.cant_update_annotation", fallback: "Can't update annotation.")
/// This document is not supported.
internal static let incompatibleDocument = L10n.tr("Localizable", "errors.pdf.incompatible_document", fallback: "This document is not supported.")
/// The combined annotation would be too large.
internal static let mergeTooBig = L10n.tr("Localizable", "errors.pdf.merge_too_big", fallback: "The combined annotation would be too large.")
/// Unable to merge annotations
Expand Down
15 changes: 0 additions & 15 deletions Zotero/Extensions/WKWebView+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,4 @@ extension WKWebView {
return Disposables.create()
}
}

static func optionalToJs(_ value: String?) -> String {
return value.flatMap({ "'" + $0 + "'" }) ?? "null"
}

/// Encodes data which need to be sent to `webView`. All data that is passed to JS is Base64 encoded so that it can be sent as a simple `String`.
static func encodeForJavascript(_ data: Data?) -> String {
return data.flatMap({ "'" + $0.base64EncodedString(options: .endLineWithLineFeed) + "'" }) ?? "null"
}

/// Encodes as JSON payload so that it can be sent to `webView`.
static func encodeAsJSONForJavascript(_ payload: Any) -> String {
let data = try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted)
return self.encodeForJavascript(data)
}
}
10 changes: 3 additions & 7 deletions Zotero/Models/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,19 @@ final class Defaults {
@CodableUserDefault(key: "SelectedRawCollectionKey", defaultValue: CollectionIdentifier.custom(.all), encoder: Defaults.jsonEncoder, decoder: Defaults.jsonDecoder)
var selectedCollectionId: CollectionIdentifier

// MARK: - Items Settings

#if MAINAPP
// MARK: - Items Settings

@CodableUserDefault(key: "RawItemsSortType", defaultValue: ItemsSortType.default, encoder: Defaults.jsonEncoder, decoder: Defaults.jsonDecoder, defaults: .standard)
var itemsSortType: ItemsSortType
#endif

// MARK: - Item Detail

#if MAINAPP
@CodableUserDefault(key: "LastUsedCreatorNamePresentation", defaultValue: .separate, encoder: Defaults.jsonEncoder, decoder: Defaults.jsonDecoder)
var creatorNamePresentation: ItemDetailState.Creator.NamePresentation
#endif

// MARK: - PDF Settings

#if MAINAPP
@UserDefault(key: "PdfReaderLineWidth", defaultValue: 2)
var activeLineWidth: Float

Expand All @@ -111,12 +107,12 @@ final class Defaults {

@CodableUserDefault(key: "PDFReaderSettings", defaultValue: PDFSettings.default, encoder: Defaults.jsonEncoder, decoder: Defaults.jsonDecoder, defaults: .standard)
var pdfSettings: PDFSettings
#endif

// MARK: - HTML / Epub Settings

@CodableUserDefault(key: "HtmlEpubReaderSettings", defaultValue: HtmlEpubSettings.default, encoder: Defaults.jsonEncoder, decoder: Defaults.jsonDecoder, defaults: .standard)
var htmlEpubSettings: HtmlEpubSettings
#endif

// MARK: - Citation / Bibliography Export

Expand Down
35 changes: 30 additions & 5 deletions Zotero/Scenes/Detail/HTML:EPUB/HtmlEpubCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ protocol HtmlEpubSidebarCoordinatorDelegate: AnyObject {
userInterfaceStyle: UIUserInterfaceStyle,
completed: @escaping (AnnotationsFilter?) -> Void
)
func showSettings(with settings: HtmlEpubSettings, sender: UIBarButtonItem, completion: @escaping (HtmlEpubSettings) -> Void)
func showSettings(with settings: HtmlEpubSettings, sender: UIBarButtonItem) -> ViewModel<ReaderSettingsActionHandler>
}

final class HtmlEpubCoordinator: Coordinator {
Expand Down Expand Up @@ -85,7 +85,8 @@ final class HtmlEpubCoordinator: Coordinator {
dbStorage: dbStorage,
schemaController: controllers.schemaController,
htmlAttributedStringConverter: controllers.htmlAttributedStringConverter,
dateParser: controllers.dateParser
dateParser: controllers.dateParser,
idleTimerController: controllers.idleTimerController
)
let state = HtmlEpubReaderState(url: url, key: key, settings: Defaults.shared.htmlEpubSettings, library: library, userId: userId, username: username)
let controller = HtmlEpubReaderViewController(
Expand Down Expand Up @@ -116,6 +117,10 @@ extension HtmlEpubCoordinator: HtmlEpubReaderCoordinatorDelegate {
title = L10n.error
message = L10n.Errors.Pdf.cantUpdateAnnotation

case .incompatibleDocument:
title = L10n.error
message = L10n.Errors.Pdf.cantUpdateAnnotation

case .unknown:
title = L10n.error
message = L10n.Errors.unknown
Expand Down Expand Up @@ -155,9 +160,6 @@ extension HtmlEpubCoordinator: HtmlEpubReaderCoordinatorDelegate {
}

extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate {
func showSettings(with settings: HtmlEpubSettings, sender: UIBarButtonItem, completion: @escaping (HtmlEpubSettings) -> Void) {
}

func showTagPicker(libraryId: LibraryIdentifier, selected: Set<String>, userInterfaceStyle: UIUserInterfaceStyle?, picked: @escaping ([Tag]) -> Void) {
guard let navigationController else { return }
(self.parentCoordinator as? DetailCoordinator)?.showTagPicker(
Expand Down Expand Up @@ -293,4 +295,27 @@ extension HtmlEpubCoordinator: HtmlEpubSidebarCoordinatorDelegate {

self.navigationController?.present(navigationController, animated: true, completion: nil)
}

func showSettings(with settings: HtmlEpubSettings, sender: UIBarButtonItem) -> ViewModel<ReaderSettingsActionHandler> {
DDLogInfo("HtmlEpubCoordinator: show settings")

let state = ReaderSettingsState(settings: settings)
let viewModel = ViewModel(initialState: state, handler: ReaderSettingsActionHandler())
let baseController = ReaderSettingsViewController(visibleRows: [.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)

return viewModel
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation

enum HtmlEpubReaderAction {
case changeFilter(AnnotationsFilter?)
case changeIdleTimerDisabled(Bool)
case deselectAnnotationDuringEditing(String)
case deselectSelectedAnnotation
case loadDocument
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct HtmlEpubReaderState: ViewModelState {
case epub(cfi: String)
}

let type: String
let buffer: String
let annotationsJson: String
let page: Page?
Expand All @@ -48,6 +49,7 @@ struct HtmlEpubReaderState: ViewModelState {
case cantDeleteAnnotation
case cantAddAnnotations
case cantUpdateAnnotation
case incompatibleDocument
case unknown
}

Expand Down
12 changes: 6 additions & 6 deletions Zotero/Scenes/Detail/HTML:EPUB/Models/HtmlEpubSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,29 @@
import UIKit

struct HtmlEpubSettings {
var interfaceStyle: UIUserInterfaceStyle
var appearance: ReaderSettingsState.Appearance
var idleTimerDisabled: Bool

static var `default`: HtmlEpubSettings {
return HtmlEpubSettings(interfaceStyle: .unspecified, idleTimerDisabled: false)
return HtmlEpubSettings(appearance: .automatic, idleTimerDisabled: false)
}
}

extension HtmlEpubSettings: Codable {
enum Keys: String, CodingKey {
case interfaceStyle
case appearance
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Keys.self)
let interfaceStyleRaw = try container.decode(Int.self, forKey: .interfaceStyle)
self.interfaceStyle = UIUserInterfaceStyle(rawValue: interfaceStyleRaw) ?? .unspecified
let appearanceRaw = try container.decode(UInt.self, forKey: .appearance)
self.appearance = ReaderSettingsState.Appearance(rawValue: appearanceRaw) ?? .automatic
// This setting is not persisted, always defaults to false
self.idleTimerDisabled = false
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Keys.self)
try container.encode(self.interfaceStyle.rawValue, forKey: .interfaceStyle)
try container.encode(self.appearance.rawValue, forKey: .appearance)
}
}
Loading

0 comments on commit d2fe8db

Please sign in to comment.