feat: chapter runtime refactoring and related updates
- Refactor chapter runtime: replace window coordinator/snapshot with warmup orchestrator - Update EPUB core: parser, reading session, JS bridge, navigator layout - Update reader controller: data source, location resolution, persistence - Update chapter runtime: data cache, loader, runtime store, disk cache, warmup orchestrator - Remove deprecated navigation state machine and pagination state - Update text rendering: book cache, HTML normalizer - Update UI: text content view, dark image adjuster, text selection controller - Update settings and reader configuration - Add CODE_REVIEW.md and AUDIT_FINAL.md documentation - Update pod dependencies (remove SSAlertSwift, SnapKit) - Update podspec and pod configuration files Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
import UIKit
|
||||
|
||||
/// Captures layout parameters on the main thread for safe use on background queues.
|
||||
/// Create via `RDEPUBReaderContext.makeLayoutSnapshot()` before dispatching work off the main thread.
|
||||
struct RDEPUBLayoutSnapshot {
|
||||
let pageSize: CGSize
|
||||
let style: RDEPUBTextRenderStyle
|
||||
let layoutConfig: RDEPUBTextLayoutConfig
|
||||
let renderSignature: String
|
||||
}
|
||||
|
||||
final class RDEPUBReaderContext {
|
||||
|
||||
private let activityLock = NSLock()
|
||||
@@ -73,11 +82,6 @@ final class RDEPUBReaderContext {
|
||||
set { state.paginationToken = newValue }
|
||||
}
|
||||
|
||||
var paginator: RDEPUBPaginator? {
|
||||
get { state.paginator }
|
||||
set { state.paginator = newValue }
|
||||
}
|
||||
|
||||
var searchState: RDEPUBSearchState? {
|
||||
get { state.searchState }
|
||||
set { state.searchState = newValue }
|
||||
@@ -159,43 +163,57 @@ final class RDEPUBReaderContext {
|
||||
}
|
||||
|
||||
func currentPreferences() -> RDEPUBPreferences {
|
||||
configuration.makePreferences()
|
||||
let safeInsets = controller?.view.safeAreaInsets ?? .zero
|
||||
return configuration.makePreferences(safeAreaInsets: safeInsets)
|
||||
}
|
||||
|
||||
/// Captures all layout parameters needed for background chapter loading.
|
||||
/// Must be called on the main thread. The returned snapshot is safe to use on any thread.
|
||||
func makeLayoutSnapshot() -> RDEPUBLayoutSnapshot {
|
||||
dispatchPrecondition(condition: .onQueue(.main))
|
||||
let pageSize = currentTextPageSize()
|
||||
let style = currentTextRenderStyle()
|
||||
let layoutConfig = currentTextLayoutConfig(pageSize: pageSize)
|
||||
let renderSignature = renderSignature(style: style, pageSize: pageSize, layoutConfig: layoutConfig)
|
||||
return RDEPUBLayoutSnapshot(
|
||||
pageSize: pageSize,
|
||||
style: style,
|
||||
layoutConfig: layoutConfig,
|
||||
renderSignature: renderSignature
|
||||
)
|
||||
}
|
||||
|
||||
func currentTextPageSize() -> CGSize {
|
||||
if Thread.isMainThread {
|
||||
let pageNum = (readerView?.currentPage ?? -1) >= 0 ? readerView?.currentPage : nil
|
||||
if let readerView, let pageNum {
|
||||
let resolvedSize = readerView.resolvedSinglePageSize(pageNum: pageNum)
|
||||
if resolvedSize.width > 0, resolvedSize.height > 0 {
|
||||
return resolvedSize
|
||||
}
|
||||
// M-04: Use dispatchPrecondition instead of assert so it's enforced in Release builds too.
|
||||
dispatchPrecondition(condition: .onQueue(.main))
|
||||
|
||||
let pageNum = (readerView?.currentPage ?? -1) >= 0 ? readerView?.currentPage : nil
|
||||
if let readerView, let pageNum {
|
||||
let resolvedSize = readerView.resolvedSinglePageSize(pageNum: pageNum)
|
||||
if resolvedSize.width > 0, resolvedSize.height > 0 {
|
||||
return resolvedSize
|
||||
}
|
||||
let viewportSize = currentLayoutContext().viewportSize
|
||||
if viewportSize.width > 0, viewportSize.height > 0 {
|
||||
return viewportSize
|
||||
}
|
||||
} else if let lastTextPaginationPageSize,
|
||||
lastTextPaginationPageSize.width > 0,
|
||||
lastTextPaginationPageSize.height > 0 {
|
||||
}
|
||||
let viewportSize = currentLayoutContext().viewportSize
|
||||
if viewportSize.width > 0, viewportSize.height > 0 {
|
||||
return viewportSize
|
||||
}
|
||||
if let lastTextPaginationPageSize,
|
||||
lastTextPaginationPageSize.width > 0,
|
||||
lastTextPaginationPageSize.height > 0 {
|
||||
return lastTextPaginationPageSize
|
||||
} else {
|
||||
let mainThreadSize = DispatchQueue.main.sync { [weak self] in
|
||||
self?.currentTextPageSize() ?? .zero
|
||||
}
|
||||
if mainThreadSize.width > 0, mainThreadSize.height > 0 {
|
||||
return mainThreadSize
|
||||
}
|
||||
}
|
||||
return environment.fallbackViewportSize
|
||||
}
|
||||
|
||||
func currentTextRenderStyle() -> RDEPUBTextRenderStyle {
|
||||
environment.currentTextRenderStyle(configuration: configuration)
|
||||
dispatchPrecondition(condition: .onQueue(.main))
|
||||
return environment.currentTextRenderStyle(configuration: configuration)
|
||||
}
|
||||
|
||||
func currentTextLayoutConfig(pageSize: CGSize) -> RDEPUBTextLayoutConfig {
|
||||
environment.currentTextLayoutConfig(configuration: configuration, pageSize: pageSize)
|
||||
dispatchPrecondition(condition: .onQueue(.main))
|
||||
return environment.currentTextLayoutConfig(configuration: configuration, pageSize: pageSize)
|
||||
}
|
||||
|
||||
func resolvedTextRenderer() -> RDEPUBTextRenderer {
|
||||
@@ -282,10 +300,19 @@ final class RDEPUBReaderContext {
|
||||
}
|
||||
|
||||
func currentRenderSignature() -> String {
|
||||
dispatchPrecondition(condition: .onQueue(.main))
|
||||
let style = currentTextRenderStyle()
|
||||
let pageSize = currentTextPageSize()
|
||||
let layoutConfig = currentTextLayoutConfig(pageSize: pageSize)
|
||||
return [
|
||||
return renderSignature(style: style, pageSize: pageSize, layoutConfig: layoutConfig)
|
||||
}
|
||||
|
||||
private func renderSignature(
|
||||
style: RDEPUBTextRenderStyle,
|
||||
pageSize: CGSize,
|
||||
layoutConfig: RDEPUBTextLayoutConfig
|
||||
) -> String {
|
||||
[
|
||||
style.font.fontName,
|
||||
"\(style.font.pointSize)",
|
||||
"\(configuration.lineHeightMultiple)",
|
||||
|
||||
Reference in New Issue
Block a user