Skip to content

Commit

Permalink
Add auto-scrolling to ContentImageView
Browse files Browse the repository at this point in the history
This commit adds the `scrolling` property to the `QuranHighlightsService` class, which allows for programmatically scrolling to a specific verse. This feature is implemented by observing changes in the `highlights` property and filtering for cases where scrolling is needed. The commit also includes necessary updates to the `ContentImageView` and `ContentImageViewModel` classes to support scrolling to the desired verse.
  • Loading branch information
mohamede1945 committed May 23, 2024
1 parent 973461b commit 0d2b69e
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 6 deletions.
10 changes: 10 additions & 0 deletions Domain/AnnotationsService/Sources/QuranHighlightsService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ public final class QuranHighlightsService {
}
}

public var scrolling: AnyPublisher<Void, Never> {
$highlights
.zip($highlights.dropFirst())
.filter { oldValue, newValue in
newValue.needsScrolling(comparingTo: oldValue)
}
.map { _ in }
.eraseToAnyPublisher()
}

public func reset() {
highlights = QuranHighlights()
}
Expand Down
10 changes: 10 additions & 0 deletions Features/QuranImageFeature/ContentImageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct ContentImageView: View {
quarterName: viewModel.page.localizedQuarterName,
suraNames: viewModel.page.suraNames(),
page: viewModel.page.localizedNumber,
scrollToItem: viewModel.scrollToItem,
onScaleChange: { viewModel.scale = $0 },
onGlobalFrameChange: { viewModel.imageFrame = $0 }
)
Expand All @@ -36,6 +37,7 @@ private struct ContentImageViewBody: View {
let quarterName: String
let suraNames: MultipartText
let page: String
let scrollToItem: WordFrameLine?
let onScaleChange: (WordFrameScale) -> Void
let onGlobalFrameChange: (CGRect) -> Void

Expand All @@ -55,6 +57,13 @@ private struct ContentImageViewBody: View {
// TODO: Should be part of the headers and footers.
.font(.footnote)
.populateReadableInsets()
.onChange(of: scrollToItem) { scrollToItem in
if let scrollToItem {
withAnimation {
scrollView.scrollTo(scrollToItem, anchor: UnitPoint(x: 0, y: 0.2))
}
}
}
}
}
}
Expand All @@ -71,6 +80,7 @@ private struct ContentImageViewBody: View {
quarterName: "ABC",
suraNames: "ABC",
page: "604",
scrollToItem: nil,
onScaleChange: { _ in },
onGlobalFrameChange: { _ in }
)
Expand Down
27 changes: 27 additions & 0 deletions Features/QuranImageFeature/ContentImageViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import QuranAnnotations
import QuranGeometry
import QuranKit
import SwiftUI
import VLogging

@MainActor
class ContentImageViewModel: ObservableObject {
Expand All @@ -29,6 +30,12 @@ class ContentImageViewModel: ObservableObject {
highlightsService.$highlights
.sink { [weak self] in self?.highlights = $0 }
.store(in: &cancellables)

highlightsService.scrolling
.sink { [weak self] in
self?.scrollToVerseIfNeeded()
}
.store(in: &cancellables)
}

// MARK: Internal
Expand All @@ -38,6 +45,7 @@ class ContentImageViewModel: ObservableObject {
@Published var suraHeaderLocations: [SuraHeaderLocation] = []
@Published var ayahNumberLocations: [AyahNumberLocation] = []
@Published var highlights: QuranHighlights
@Published var scrollToItem: WordFrameLine?

@Published var scale: WordFrameScale = .zero
@Published var imageFrame: CGRect = .zero
Expand Down Expand Up @@ -73,6 +81,8 @@ class ContentImageViewModel: ObservableObject {
suraHeaderLocations = try await imageDataService.suraHeaders(page)
ayahNumberLocations = try await imageDataService.ayahNumbers(page)
}

scrollToVerseIfNeeded()
} catch {
// TODO: should show error to the user
crasher.recordError(error, reason: "Failed to retrieve quran image details")
Expand All @@ -93,4 +103,21 @@ class ContentImageViewModel: ObservableObject {
private let highlightsService: QuranHighlightsService
private let reading: Reading
private var cancellables: Set<AnyCancellable> = []

private func scrollToVerseIfNeededSynchronously() {
guard let ayah = highlightsService.highlights.firstScrollingVerse() else {
return
}
if let line = imagePage?.wordFrames.lineFramesVerVerse(ayah).first {
logger.info("Quran Image: scrollToVerseIfNeeded \(ayah) - \(line.frames)")
scrollToItem = line
}
}

private func scrollToVerseIfNeeded() {
// Execute in the next runloop to allow the highlightsService value to load.
DispatchQueue.main.async {
self.scrollToVerseIfNeededSynchronously()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,9 @@ public final class ContentTranslationViewModel: ObservableObject {
.sink { [weak self] in self?.highlights = $0.versesByHighlights().mapValues { Color($0) } }
.store(in: &cancellables)

highlightsService.$highlights
.zip(highlightsService.$highlights.dropFirst())
.sink { [weak self] oldValue, newValue in
if newValue.needsScrolling(comparingTo: oldValue) {
self?.scrollToVerseIfNeeded()
}
highlightsService.scrolling
.sink { [weak self] in
self?.scrollToVerseIfNeeded()
}
.store(in: &cancellables)

Expand Down

0 comments on commit 0d2b69e

Please sign in to comment.