chore: checkpoint current milestone work
This commit is contained in:
@@ -25,6 +25,8 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
case pendingRepagination
|
||||
}
|
||||
|
||||
private typealias NativeTextSnapshot = (pages: [EPUBPage], chapters: [EPUBChapterInfo])
|
||||
|
||||
public weak var delegate: RDEPUBReaderDelegate?
|
||||
|
||||
public var configuration: RDEPUBReaderConfiguration {
|
||||
@@ -610,9 +612,10 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
|
||||
private func applyTextBook(_ textBook: RDEPUBTextBook, restoreLocation: RDEPUBLocation?) {
|
||||
self.textBook = textBook
|
||||
self.activePages = []
|
||||
self.activeChapters = []
|
||||
readingSession?.setActiveSnapshot((pages: [], chapters: []))
|
||||
let snapshot = nativeTextSnapshot(from: textBook)
|
||||
self.activePages = snapshot.pages
|
||||
self.activeChapters = snapshot.chapters
|
||||
readingSession?.setActiveSnapshot(snapshot)
|
||||
|
||||
guard !textBook.pages.isEmpty else {
|
||||
handle(error: RDEPUBParserError.emptySpine)
|
||||
@@ -667,41 +670,28 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
|
||||
@discardableResult
|
||||
fileprivate func restoreReadingLocation(_ location: RDEPUBLocation, animated: Bool = false) -> Bool {
|
||||
if let textBook {
|
||||
guard let publication, let targetPageNumber = textBook.pageNumber(
|
||||
for: location,
|
||||
resolver: publication.resourceResolver,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
) else {
|
||||
readerView.transitionToPage(pageNum: 0)
|
||||
readingSession?.transition(to: .idle)
|
||||
return false
|
||||
}
|
||||
readerView.transitionToPage(pageNum: max(targetPageNumber - 1, 0), animated: animated)
|
||||
guard let targetPageNumber = pageNumber(for: location) else {
|
||||
readerView.transitionToPage(pageNum: 0)
|
||||
readingSession?.transition(to: .idle)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
guard let readingSession,
|
||||
let targetPageNumber = readingSession.queueNavigation(
|
||||
if textBook == nil {
|
||||
_ = readingSession?.queueNavigation(
|
||||
to: location,
|
||||
relativeToSpineIndex: nil,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
) else {
|
||||
readerView.transitionToPage(pageNum: 0)
|
||||
self.readingSession?.transition(to: .idle)
|
||||
return false
|
||||
)
|
||||
} else {
|
||||
readingSession?.transition(to: .jumping)
|
||||
}
|
||||
readerView.transitionToPage(pageNum: max(targetPageNumber - 1, 0), animated: animated)
|
||||
return true
|
||||
}
|
||||
|
||||
fileprivate func currentVisibleLocation() -> RDEPUBLocation? {
|
||||
if let textBook, readerView.currentPage >= 0 {
|
||||
return textBook.location(
|
||||
forPageNumber: readerView.currentPage + 1,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
)
|
||||
if textBook != nil, readerView.currentPage >= 0 {
|
||||
return resolvedTextLocation(forPageNumber: readerView.currentPage + 1)
|
||||
}
|
||||
return readingSession?.currentReadingLocation(bookIdentifier: currentBookIdentifier)
|
||||
}
|
||||
@@ -1494,6 +1484,17 @@ extension RDEPUBReaderController {
|
||||
}
|
||||
|
||||
private func pageNumber(for searchMatch: RDEPUBSearchMatch) -> Int? {
|
||||
if let textBook,
|
||||
let publication,
|
||||
let rangeLocation = searchMatch.rangeLocation,
|
||||
let chapter = textBook.chapters.first(where: {
|
||||
(publication.resourceResolver.normalizedHref($0.href) ?? $0.href) ==
|
||||
(publication.resourceResolver.normalizedHref(searchMatch.href) ?? searchMatch.href)
|
||||
}),
|
||||
let page = chapter.pages.first(where: { rangeLocation >= $0.pageStartOffset && rangeLocation <= $0.pageEndOffset }) {
|
||||
return page.absolutePageIndex + 1
|
||||
}
|
||||
|
||||
let location = RDEPUBLocation(
|
||||
bookIdentifier: currentBookIdentifier,
|
||||
href: searchMatch.href,
|
||||
@@ -1589,7 +1590,13 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
|
||||
}
|
||||
|
||||
private func textHighlights(for page: RDEPUBTextPage) -> [RDEPUBHighlight] {
|
||||
activeHighlights.filter { $0.location.href == page.href }
|
||||
guard let publication else {
|
||||
return activeHighlights.filter { $0.location.href == page.href }
|
||||
}
|
||||
let pageHref = publication.resourceResolver.normalizedHref(page.href) ?? page.href
|
||||
return activeHighlights.filter {
|
||||
(publication.resourceResolver.normalizedHref($0.location.href) ?? $0.location.href) == pageHref
|
||||
}
|
||||
}
|
||||
|
||||
public func topToolView(readerView: RDReaderView) -> UIView? {
|
||||
@@ -1608,10 +1615,10 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
|
||||
delegate?.epubReaderDidReachEnd(self)
|
||||
}
|
||||
|
||||
if let textBook,
|
||||
let location = textBook.location(forPageNumber: pageNum + 1, bookIdentifier: currentBookIdentifier) {
|
||||
if textBook != nil,
|
||||
let location = resolvedTextLocation(forPageNumber: pageNum + 1) {
|
||||
persist(location: location)
|
||||
readingSession?.transition(to: .idle)
|
||||
synchronizeTextReadingState(pageNumber: pageNum + 1, location: location)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1707,16 +1714,18 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate {
|
||||
return scopedSelection(selection, relativeToSpineIndex: nil)
|
||||
}
|
||||
|
||||
let lastOffset = max(chapter.attributedContent.length - 1, 1)
|
||||
let start = max(0, min(payload.start, lastOffset))
|
||||
let end = max(start, min(payload.end, lastOffset))
|
||||
let contentLength = max(chapter.attributedContent.length, 1)
|
||||
let lastInclusiveOffset = max(contentLength - 1, 1)
|
||||
let start = max(0, min(payload.start, lastInclusiveOffset))
|
||||
let endExclusive = max(start + 1, min(payload.end, contentLength))
|
||||
let lastSelectedOffset = max(start, min(endExclusive - 1, lastInclusiveOffset))
|
||||
return RDEPUBSelection(
|
||||
bookIdentifier: currentBookIdentifier,
|
||||
location: RDEPUBLocation(
|
||||
bookIdentifier: currentBookIdentifier,
|
||||
href: selection.location.href,
|
||||
progression: Double(start) / Double(lastOffset),
|
||||
lastProgression: Double(end) / Double(lastOffset),
|
||||
progression: Double(start) / Double(lastInclusiveOffset),
|
||||
lastProgression: Double(lastSelectedOffset) / Double(lastInclusiveOffset),
|
||||
fragment: nil
|
||||
),
|
||||
text: selection.text,
|
||||
@@ -1724,6 +1733,81 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate {
|
||||
createdAt: selection.createdAt
|
||||
)
|
||||
}
|
||||
|
||||
private func pageNumber(for location: RDEPUBLocation) -> Int? {
|
||||
if let textBook, let publication {
|
||||
let normalizedLocation = publication.resourceResolver.normalizedLocation(
|
||||
location,
|
||||
relativeToSpineIndex: nil,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
) ?? location
|
||||
return textBook.pageNumber(
|
||||
for: normalizedLocation,
|
||||
resolver: publication.resourceResolver,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
)
|
||||
}
|
||||
|
||||
return readingSession?.queueNavigation(
|
||||
to: location,
|
||||
relativeToSpineIndex: nil,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
)
|
||||
}
|
||||
|
||||
private func resolvedTextLocation(forPageNumber pageNumber: Int) -> RDEPUBLocation? {
|
||||
guard let textBook,
|
||||
let publication,
|
||||
let location = textBook.location(
|
||||
forPageNumber: pageNumber,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return publication.resourceResolver.normalizedLocation(
|
||||
location,
|
||||
relativeToSpineIndex: nil,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
) ?? location
|
||||
}
|
||||
|
||||
private func synchronizeTextReadingState(pageNumber: Int, location: RDEPUBLocation) {
|
||||
guard let textBook,
|
||||
let page = textBook.page(at: pageNumber) else {
|
||||
readingSession?.transition(to: .idle)
|
||||
return
|
||||
}
|
||||
|
||||
readingSession?.updateReadingContext(
|
||||
pageNumber: pageNumber,
|
||||
location: location,
|
||||
spineIndex: page.spineIndex,
|
||||
chapterIndex: page.chapterIndex,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
)
|
||||
}
|
||||
|
||||
private func nativeTextSnapshot(from textBook: RDEPUBTextBook) -> NativeTextSnapshot {
|
||||
let chapters = textBook.chapters.map {
|
||||
EPUBChapterInfo(
|
||||
spineIndex: $0.spineIndex,
|
||||
title: $0.title,
|
||||
pageCount: $0.pages.count
|
||||
)
|
||||
}
|
||||
let pages = textBook.pages.map {
|
||||
EPUBPage(
|
||||
spineIndex: $0.spineIndex,
|
||||
chapterIndex: $0.chapterIndex,
|
||||
pageIndexInChapter: $0.pageIndexInChapter,
|
||||
totalPagesInChapter: $0.totalPagesInChapter,
|
||||
chapterTitle: $0.chapterTitle,
|
||||
fixedSpread: nil
|
||||
)
|
||||
}
|
||||
return (pages, chapters)
|
||||
}
|
||||
}
|
||||
|
||||
extension RDEPUBReaderController: UIGestureRecognizerDelegate {
|
||||
|
||||
@@ -127,8 +127,9 @@ final class RDEPUBTextContentView: UIView {
|
||||
}
|
||||
|
||||
private func applyHighlights(to content: NSMutableAttributedString, page: RDEPUBTextPage) {
|
||||
let pageStart = Int(page.pageStartOffset)
|
||||
let pageEndExclusive = Int(page.pageEndOffset) + 1
|
||||
let pageRange = absoluteOffsetRange(for: page)
|
||||
let pageStart = pageRange.lowerBound
|
||||
let pageEndExclusive = pageRange.upperBound
|
||||
|
||||
for highlight in highlightedRanges where highlight.location.href == page.href {
|
||||
guard let range = RDEPUBTextOffsetRangeInfo.decode(from: highlight.rangeInfo)?.nsRange else { continue }
|
||||
@@ -165,8 +166,9 @@ final class RDEPUBTextContentView: UIView {
|
||||
|
||||
let normalColor = UIColor(red: 248 / 255, green: 225 / 255, blue: 108 / 255, alpha: 0.55)
|
||||
let activeColor = UIColor(red: 255 / 255, green: 159 / 255, blue: 67 / 255, alpha: 0.75)
|
||||
let pageStart = Int(page.pageStartOffset)
|
||||
let pageEndExclusive = Int(page.pageEndOffset) + 1
|
||||
let pageRange = absoluteOffsetRange(for: page)
|
||||
let pageStart = pageRange.lowerBound
|
||||
let pageEndExclusive = pageRange.upperBound
|
||||
|
||||
for match in searchState.matches where match.href == page.href {
|
||||
guard let matchStart = match.rangeLocation else { continue }
|
||||
@@ -181,6 +183,12 @@ final class RDEPUBTextContentView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
private func absoluteOffsetRange(for page: RDEPUBTextPage) -> Range<Int> {
|
||||
let lowerBound = page.pageStartOffset
|
||||
let upperBound = page.pageEndOffset + 1
|
||||
return lowerBound..<max(upperBound, lowerBound)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension RDEPUBTextContentView: UITextViewDelegate {
|
||||
|
||||
Reference in New Issue
Block a user