ReadKeep/readeck/UI/SpeechPlayer/SpeechPlayerViewModel.swift
Ilyas Hallak 8d4b08da11 Add TTS feature toggle, refactor settings, and improve UI
- Implemented a toggle for the 'Read Aloud' (TTS) feature in the general settings.
- Refactored AppSettings and PlayerUIState to support TTS enable/disable.
- Updated BookmarkDetailView, PadSidebarView, PhoneTabView, and GlobalPlayerContainerView to respect the TTS setting.
- Added new RButton component for consistent button styling.
- Improved LabelsView to support tag selection on iPad and iPhone.
- Updated SettingsGeneralView and SettingsGeneralViewModel for new TTS logic and removed unused app info code.
- Added app info section to SettingsContainerView.
- Updated SettingsServerView to use English labels and messages.
- Refactored SpeechPlayerViewModel to only initialize TTS when enabled.
- Updated Core Data model to include enableTTS in SettingEntity.
- Removed obsolete files (PersistenceController.swift, old PlayerUIState).
- Various bugfixes, code cleanups, and UI improvements.
2025-07-21 23:37:37 +02:00

105 lines
3.0 KiB
Swift

import Foundation
import Combine
class SpeechPlayerViewModel: ObservableObject {
private var ttsManager: TTSManager? = nil
private var speechQueue: SpeechQueue? = nil
private let loadSettingsUseCase: PLoadSettingsUseCase
private var cancellables = Set<AnyCancellable>()
@Published var isSpeaking: Bool = false
@Published var currentText: String = ""
@Published var queueCount: Int = 0
@Published var queueItems: [SpeechQueueItem] = []
@Published var hasItems: Bool = false
@Published var progress: Double = 0.0
@Published var currentUtteranceIndex: Int = 0
@Published var totalUtterances: Int = 0
@Published var articleProgress: Double = 0.0
@Published var volume: Float = 1.0
@Published var rate: Float = 0.5
init(_ factory: UseCaseFactory = DefaultUseCaseFactory.shared) {
loadSettingsUseCase = factory.makeLoadSettingsUseCase()
}
func setup() async {
let settings = try? await loadSettingsUseCase.execute()
if settings?.enableTTS == true {
self.ttsManager = .shared
self.speechQueue = .shared
setupBindings()
}
}
private func setupBindings() {
// TTSManager bindings
ttsManager?.$isSpeaking
.assign(to: \.isSpeaking, on: self)
.store(in: &cancellables)
ttsManager?.$currentUtterance
.assign(to: \.currentText, on: self)
.store(in: &cancellables)
// SpeechQueue bindings
speechQueue?.$queueItems
.assign(to: \.queueItems, on: self)
.store(in: &cancellables)
speechQueue?.$queueItems
.map { $0.count }
.assign(to: \.queueCount, on: self)
.store(in: &cancellables)
speechQueue?.$hasItems
.assign(to: \.hasItems, on: self)
.store(in: &cancellables)
// TTS Progress bindings
ttsManager?.$progress
.assign(to: \.progress, on: self)
.store(in: &cancellables)
ttsManager?.$currentUtteranceIndex
.assign(to: \.currentUtteranceIndex, on: self)
.store(in: &cancellables)
ttsManager?.$totalUtterances
.assign(to: \.totalUtterances, on: self)
.store(in: &cancellables)
ttsManager?.$articleProgress
.assign(to: \.articleProgress, on: self)
.store(in: &cancellables)
ttsManager?.$volume
.assign(to: \.volume, on: self)
.store(in: &cancellables)
ttsManager?.$rate
.assign(to: \.rate, on: self)
.store(in: &cancellables)
}
func setVolume(_ newVolume: Float) {
ttsManager?.setVolume(newVolume)
}
func setRate(_ newRate: Float) {
ttsManager?.setRate(newRate)
}
func pause() {
ttsManager?.pause()
}
func resume() {
ttsManager?.resume()
}
func stop() {
ttsManager?.stop()
}
}