Skip to content

Commit

Permalink
Create QuranHighlightsService to manage verse highlights (#612)
Browse files Browse the repository at this point in the history
  • Loading branch information
mohamede1945 authored Dec 24, 2023
1 parent f5520e6 commit 4ac7338
Show file tree
Hide file tree
Showing 22 changed files with 236 additions and 207 deletions.
28 changes: 28 additions & 0 deletions Domain/AnnotationsService/Sources/QuranHighlightsService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// QuranHighlightsService.swift
//
//
// Created by Mohamed Afifi on 2023-12-23.
//

import Combine
import QuranAnnotations
import VLogging

public final class QuranHighlightsService {
// MARK: Lifecycle

public init() { }

// MARK: Public

@Published public var highlights = QuranHighlights() {
didSet {
logger.info("Highlights updated")
}
}

public func reset() {
highlights = QuranHighlights()
}
}
9 changes: 6 additions & 3 deletions Features/QuranContentFeature/ContentBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import UIKit
public struct ContentBuilder {
// MARK: Lifecycle

public init(container: AppDependencies) {
public init(container: AppDependencies, highlightsService: QuranHighlightsService) {
self.container = container
self.highlightsService = highlightsService
}

// MARK: Public
Expand All @@ -33,8 +34,9 @@ public struct ContentBuilder {
noteService: noteService,
lastPageUpdater: lastPageUpdater,
quran: quran,
imageDataSourceBuilder: ContentImageBuilder(container: container),
translationDataSourceBuilder: ContentTranslationBuilder(container: container)
highlightsService: highlightsService,
imageDataSourceBuilder: ContentImageBuilder(container: container, highlightsService: highlightsService),
translationDataSourceBuilder: ContentTranslationBuilder(container: container, highlightsService: highlightsService)
)
let viewModel = ContentViewModel(deps: interactorDeps, input: input)

Expand All @@ -47,4 +49,5 @@ public struct ContentBuilder {
// MARK: Private

private let container: AppDependencies
private let highlightsService: QuranHighlightsService
}
25 changes: 14 additions & 11 deletions Features/QuranContentFeature/ContentViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ final class ContentViewController: UIViewController, UIGestureRecognizerDelegate
setUpPagingStrategyChanges()
setUpDataSourceChanges()
setUpBackgroundListener()
setUpQuranUITraitsListener()
setUpHighlightsListener()
}

func gestureRecognizer(
Expand Down Expand Up @@ -84,26 +84,29 @@ final class ContentViewController: UIViewController, UIGestureRecognizerDelegate
private var pageController: PageController?
private let viewModel: ContentViewModel
private var cancellables: Set<AnyCancellable> = []
private var lastHighlights: QuranHighlights?

// MARK: - Scrolling

private func setUpQuranUITraitsListener() {
viewModel.$quranUITraits
private func setUpHighlightsListener() {
viewModel.deps.highlightsService.$highlights
.receive(on: DispatchQueue.main)
.sink { [weak self] newTraits in
self?.quranUITraitsUpdatedTo(newTraits)
.sink { [weak self] newHighlights in
self?.highlightsUpdatedTo(newHighlights)
}
.store(in: &cancellables)
}

private func quranUITraitsUpdatedTo(_ quranUITraits: QuranUITraits) {
guard let dataSource = viewModel.dataSource else {
private func highlightsUpdatedTo(_ highlights: QuranHighlights) {
defer {
lastHighlights = highlights
}

guard let oldValue = lastHighlights else {
return
}
let oldValue = dataSource.quranUITraits
dataSource.quranUITraits = quranUITraits

if let ayah = quranUITraits.highlights.verseToScrollTo(comparingTo: oldValue.highlights) {
if let ayah = highlights.verseToScrollTo(comparingTo: oldValue) {
scrollTo(page: ayah.page, animated: true, forceReload: false)
}
}
Expand Down Expand Up @@ -179,7 +182,7 @@ final class ContentViewController: UIViewController, UIGestureRecognizerDelegate

dataSource.scrollToPage(viewModel.lastViewedPage, animated: false, forceReload: true)
viewModel.visiblePagesLoaded()
quranUITraitsUpdatedTo(viewModel.quranUITraits)
highlightsUpdatedTo(viewModel.deps.highlightsService.highlights)
}

// MARK: - Gestures
Expand Down
28 changes: 10 additions & 18 deletions Features/QuranContentFeature/ContentViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public final class ContentViewModel {
let lastPageUpdater: LastPageUpdater
let quran: Quran

let highlightsService: QuranHighlightsService

let imageDataSourceBuilder: PageDataSourceBuilder
let translationDataSourceBuilder: PageDataSourceBuilder
}
Expand All @@ -61,21 +63,12 @@ public final class ContentViewModel {
twoPagesEnabled = deps.quranContentStatePreferences.twoPagesEnabled
verticalScrollingEnabled = deps.quranContentStatePreferences.verticalScrollingEnabled

quranUITraits.translationFontSize = deps.fontSizePreferences.translationFontSize
quranUITraits.arabicFontSize = deps.fontSizePreferences.arabicFontSize

deps.quranContentStatePreferences.$twoPagesEnabled
.sink { [weak self] in self?.twoPagesEnabled = $0 }
.store(in: &cancellables)
deps.quranContentStatePreferences.$verticalScrollingEnabled
.sink { [weak self] in self?.verticalScrollingEnabled = $0 }
.store(in: &cancellables)
deps.fontSizePreferences.$arabicFontSize
.sink { [weak self] in self?.quranUITraits.arabicFontSize = $0 }
.store(in: &cancellables)
deps.fontSizePreferences.$translationFontSize
.sink { [weak self] in self?.quranUITraits.translationFontSize = $0 }
.store(in: &cancellables)
deps.quranContentStatePreferences.$quranMode
.sink { [weak self] _ in self?.loadNewElementModule() }
.store(in: &cancellables)
Expand All @@ -89,8 +82,6 @@ public final class ContentViewModel {

// MARK: Public

@Published public private(set) var quranUITraits = QuranUITraits()

public var visiblePages: [Page] { dataSource?.visiblePages ?? [] }

public func removeAyahMenuHighlight() {
Expand All @@ -103,15 +94,15 @@ public final class ContentViewModel {
}

public func highlightWord(_ word: Word?) {
quranUITraits.highlights.pointedWord = word
deps.highlightsService.highlights.pointedWord = word
}

public func word(at point: CGPoint, in view: UIView) -> Word? {
dataSource?.word(at: point, in: view)
}

public func highlightReadingAyah(_ ayah: AyahNumber?) {
quranUITraits.highlights.readingVerses = [ayah].compactMap { $0 }
deps.highlightsService.highlights.readingVerses = [ayah].compactMap { $0 }
}

// MARK: Internal
Expand All @@ -121,6 +112,8 @@ public final class ContentViewModel {
@Published var twoPagesEnabled: Bool
@Published var dataSource: PageDataSource?

let deps: Deps

var verticalScrollingEnabled: Bool {
didSet { loadNewElementModule() }
}
Expand Down Expand Up @@ -154,7 +147,7 @@ public final class ContentViewModel {

func visiblePagesUpdated() {
// remove search highlight when page changes
quranUITraits.highlights.searchVerses = []
deps.highlightsService.highlights.searchVerses = []
visiblePagesLoaded()
}

Expand Down Expand Up @@ -205,13 +198,12 @@ public final class ContentViewModel {

private var cancellables: Set<AnyCancellable> = []

private let deps: Deps
private let input: QuranInput
private var pages: [Page]

private var longPressData: LongPressData? {
didSet {
quranUITraits.highlights.shareVerses = selectedVerses ?? []
deps.highlightsService.highlights.shareVerses = selectedVerses ?? []
}
}

Expand Down Expand Up @@ -247,7 +239,7 @@ public final class ContentViewModel {
private func configureAsInitialPage() {
deps.lastPageUpdater.configure(initialPage: input.initialPage, lastPage: input.lastPage)
loadNewElementModule()
quranUITraits.highlights.searchVerses = [input.highlightingSearchAyah].compactMap { $0 }
deps.highlightsService.highlights.searchVerses = [input.highlightingSearchAyah].compactMap { $0 }
}

private func loadNewElementModule() {
Expand All @@ -267,7 +259,7 @@ public final class ContentViewModel {
deps.noteService.notes(quran: deps.quran)
.map { notes in notes.flatMap { note in note.verses.map { ($0, note) } } }
.receive(on: DispatchQueue.main)
.sink { [weak self] in self?.quranUITraits.highlights.noteVerses = Self.dictionaryFrom($0) }
.sink { [weak self] in self?.deps.highlightsService.highlights.noteVerses = Self.dictionaryFrom($0) }
.store(in: &cancellables)
}
}
Expand Down
8 changes: 6 additions & 2 deletions Features/QuranImageFeature/ContentImageBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2019 Quran.com. All rights reserved.
//

import AnnotationsService
import AppDependencies
import Caching
import Foundation
Expand All @@ -23,8 +24,9 @@ import VLogging
public struct ContentImageBuilder: PageDataSourceBuilder {
// MARK: Lifecycle

public init(container: AppDependencies) {
public init(container: AppDependencies, highlightsService: QuranHighlightsService) {
self.container = container
self.highlightsService = highlightsService
}

// MARK: Public
Expand All @@ -45,7 +47,8 @@ public struct ContentImageBuilder: PageDataSourceBuilder {
let controller = ContentImageViewController(
page: page,
dataService: cacheableImageService,
pageMarkerService: cacheablePageMarkers
pageMarkerService: cacheablePageMarkers,
highlightsService: highlightsService
)
return controller
}
Expand All @@ -54,6 +57,7 @@ public struct ContentImageBuilder: PageDataSourceBuilder {
// MARK: Private

private let container: AppDependencies
private let highlightsService: QuranHighlightsService

private func readingDirectory(_ reading: Reading) -> URL {
let remoteResource = container.remoteResources?.resource(for: reading)
Expand Down
66 changes: 39 additions & 27 deletions Features/QuranImageFeature/ContentImageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
// Copyright © 2019 Quran.com. All rights reserved.
//

import AnnotationsService
import Combine
import ImageService
import Localization
import NoorUI
Expand All @@ -19,21 +21,13 @@ import VLogging
class ContentImageView: UIView {
// MARK: Lifecycle

init() {
init(highlightsService: QuranHighlightsService) {
self.highlightsService = highlightsService
imageView = ContentImageContentView(topView: topView, bottomView: bottomView, fullWindowView: true)
super.init(frame: .zero)

addAutoLayoutSubview(imageView)
imageView.vc.edges()

setupTopView(topView)
setupBottomView(bottomView)

for label in [juzLabel, suraLabel, pageLabel] {
label.font = UIFont.systemFont(ofSize: 14)
label.textColor = .label
}
pageLabel.textAlignment = .center
setUpViews()
setUpHighlights()
}

@available(*, unavailable)
Expand All @@ -43,20 +37,6 @@ class ContentImageView: UIView {

// MARK: Internal

// MARK: - PageView

var quranUITraits = QuranUITraits() {
didSet {
logger.info("Quran Image: quranUITraits changed")

highlightingView.highlights = quranUITraits.highlights

if quranUITraits.highlights.needsScrolling(comparingTo: oldValue.highlights) {
scrollToVerseIfNeeded()
}
}
}

var page: Page? {
didSet {
imageView.image = nil
Expand Down Expand Up @@ -103,6 +83,9 @@ class ContentImageView: UIView {
private let suraLabel = UILabel()
private let pageLabel = UILabel()

private let highlightsService: QuranHighlightsService
private var cancellables: Set<AnyCancellable> = []

private var highlightingView: QuranImageHighlightingView {
imageView.highlightingView
}
Expand All @@ -111,6 +94,35 @@ class ContentImageView: UIView {
imageView.plainView.scrollView
}

private func setUpViews() {
addAutoLayoutSubview(imageView)
imageView.vc.edges()

setupTopView(topView)
setupBottomView(bottomView)

for label in [juzLabel, suraLabel, pageLabel] {
label.font = UIFont.systemFont(ofSize: 14)
label.textColor = .label
}
pageLabel.textAlignment = .center
}

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

highlightsService.$highlights
.sink { [weak self] in self?.highlightingView.highlights = $0 }
.store(in: &cancellables)
}

private func setupTopView(_ topView: UIView) {
topView.addAutoLayoutSubview(juzLabel)
juzLabel.vc.verticalEdges()
Expand All @@ -131,7 +143,7 @@ class ContentImageView: UIView {
}

private func scrollToVerseIfNeededSynchronously() {
guard let ayah = quranUITraits.highlights.firstScrollingVerse() else {
guard let ayah = highlightsService.highlights.firstScrollingVerse() else {
return
}

Expand Down
Loading

0 comments on commit 4ac7338

Please sign in to comment.