All checks were successful
CI / Scraper / Lint (push) Successful in 10s
CI / Scraper / Test (push) Successful in 14s
Release / Scraper / Test (push) Successful in 18s
CI / Scraper / Lint (pull_request) Successful in 18s
Release / UI / Build (push) Successful in 23s
CI / Scraper / Test (pull_request) Successful in 15s
CI / UI / Build (pull_request) Successful in 32s
Release / Scraper / Docker (push) Successful in 55s
CI / Scraper / Docker Push (pull_request) Has been skipped
CI / UI / Docker Push (pull_request) Has been skipped
CI / Scraper / Docker Push (push) Successful in 1m5s
Release / UI / Docker (push) Successful in 1m12s
iOS CI / Build (push) Successful in 4m18s
iOS CI / Build (pull_request) Successful in 4m25s
iOS CI / Test (push) Successful in 8m11s
iOS CI / Test (pull_request) Successful in 8m21s
70 lines
2.0 KiB
Swift
70 lines
2.0 KiB
Swift
import Foundation
|
|
|
|
// MARK: - ChapterReaderViewModel
|
|
|
|
@Observable @MainActor
|
|
final class ChapterReaderViewModel {
|
|
let slug: String
|
|
private(set) var chapter: Int
|
|
|
|
var content: ChapterResponse?
|
|
var isLoading = false
|
|
var error: String?
|
|
|
|
init(slug: String, chapter: Int) {
|
|
self.slug = slug
|
|
self.chapter = chapter
|
|
}
|
|
|
|
/// Switch to a different chapter in-place; `chapter` change causes `.task(id: chapter)` to re-fire `load()`.
|
|
func switchChapter(to newChapter: Int) {
|
|
guard newChapter != chapter else { return }
|
|
chapter = newChapter
|
|
content = nil
|
|
error = nil
|
|
}
|
|
|
|
func load() async {
|
|
isLoading = true
|
|
error = nil
|
|
do {
|
|
content = try await APIClient.shared.chapterContent(slug: slug, chapter: chapter)
|
|
try? await APIClient.shared.setProgress(slug: slug, chapter: chapter)
|
|
} catch {
|
|
if !(error is CancellationError) {
|
|
self.error = error.localizedDescription
|
|
}
|
|
}
|
|
isLoading = false
|
|
}
|
|
|
|
func toggleAudio(audioPlayer: AudioPlayerService, settings: UserSettings) {
|
|
guard let content else { return }
|
|
|
|
let isCurrent = audioPlayer.isActive
|
|
&& audioPlayer.slug == slug
|
|
&& audioPlayer.chapter == chapter
|
|
|
|
if isCurrent {
|
|
audioPlayer.togglePlayPause()
|
|
} else {
|
|
let voice = BookVoicePreferences.shared.voiceWithFallback(
|
|
for: slug,
|
|
globalVoice: settings.voice
|
|
)
|
|
audioPlayer.load(
|
|
slug: slug,
|
|
chapter: chapter,
|
|
chapterTitle: content.chapter.title,
|
|
bookTitle: content.book.title,
|
|
coverURL: content.book.cover,
|
|
voice: voice,
|
|
speed: settings.speed,
|
|
chapters: content.chapters,
|
|
nextChapter: content.next,
|
|
prevChapter: content.prev
|
|
)
|
|
}
|
|
}
|
|
}
|