Auto commit Sat Mar 21 11:01:02 PM CDT 2026

This commit is contained in:
Tretzi
2026-03-21 23:01:02 -05:00
parent 5c48387dc2
commit 869c98a47c
11 changed files with 354 additions and 104 deletions

View File

@@ -9,7 +9,6 @@ import com.mindmachine.mvp.data.SettingsRepository
import com.mindmachine.mvp.data.UserProgramEntity
import com.mindmachine.mvp.data.UserProgramRepository
import com.mindmachine.mvp.domain.AppSettings
import com.mindmachine.mvp.domain.CountdownPreference
import com.mindmachine.mvp.domain.Presets
import com.mindmachine.mvp.domain.RuntimeState
import com.mindmachine.mvp.domain.SessionConfig
@@ -59,15 +58,27 @@ class MainViewModel(
private var runJob: Job? = null
private var elapsedBeforePauseSec: Float = 0f
private var cachedUserPrograms: List<UserProgramEntity> = emptyList()
private var cachedPrograms: List<UserProgramEntity> = emptyList()
init {
viewModelScope.launch {
combine(settingsRepository.settings, userProgramRepository.userPrograms) { settings, userPrograms ->
settings to userPrograms
}.collect { (settings, userPrograms) ->
cachedUserPrograms = userPrograms
val userPresets = userPrograms.map {
val builtInPrograms = Presets.builtIn.map { preset ->
userPrograms.firstOrNull { it.id == preset.id } ?: UserProgramEntity(
id = preset.id,
name = preset.name,
description = preset.description,
timeline = TimelineProgramFactory.fromPreset(preset),
sortOrder = preset.sortOrder,
)
}
val mergedPrograms = (builtInPrograms + userPrograms.filterNot { program -> Presets.builtIn.any { it.id == program.id } })
.sortedBy { it.sortOrder }
cachedPrograms = mergedPrograms
val allPresets = mergedPrograms.map {
TimelineProgramFactory.toPreset(
id = it.id,
name = it.name,
@@ -77,7 +88,6 @@ class MainViewModel(
isUserProgram = true,
)
}
val allPresets = (Presets.builtIn + userPresets).sortedBy { it.sortOrder }
_ui.update { current ->
val selectedId = settings.lastPresetId ?: current.selectedPreset.id
@@ -88,7 +98,7 @@ class MainViewModel(
presets = allPresets,
selectedPreset = selectedPreset,
config = current.config.copy(mode = settings.defaultModePreference),
timeline = if (sameSelection) current.timeline else timelineForPreset(selectedPreset, userPrograms),
timeline = if (sameSelection) current.timeline else timelineForProgramId(selectedPreset.id, mergedPrograms),
)
}
}
@@ -106,7 +116,7 @@ class MainViewModel(
it.copy(
selectedPreset = preset,
config = cfg.copy(durationSec = cfg.durationSec.coerceIn(60, 8 * 60 * 60)),
timeline = timelineForPreset(preset, cachedUserPrograms),
timeline = timelineForProgramId(preset.id, cachedPrograms),
error = null,
)
}
@@ -143,8 +153,8 @@ class MainViewModel(
}
val state = _ui.value
val id = if (state.selectedPreset.isUserProgram) state.selectedPreset.id else "user-${UUID.randomUUID()}"
val sortOrder = cachedUserPrograms.find { it.id == id }?.sortOrder ?: nextSortOrder()
val id = state.selectedPreset.id
val sortOrder = cachedPrograms.find { it.id == id }?.sortOrder ?: nextSortOrder()
userProgramRepository.upsert(
UserProgramEntity(
@@ -176,13 +186,13 @@ class MainViewModel(
fun deleteProgram(id: String) = viewModelScope.launch {
userProgramRepository.delete(id)
if (_ui.value.selectedPreset.id == id) {
val fallback = Presets.builtIn.first()
val fallback = _ui.value.presets.firstOrNull { it.id != id } ?: Presets.builtIn.first()
settingsRepository.updateLastPreset(fallback.id)
_ui.update {
it.copy(
selectedPreset = fallback,
config = fallback.toConfig(it.settings.defaultModePreference),
timeline = TimelineProgramFactory.fromPreset(fallback),
timeline = timelineForProgramId(fallback.id, cachedPrograms),
error = null,
)
}
@@ -212,9 +222,10 @@ class MainViewModel(
fun setCarrier(value: Float) = _ui.update { it.copy(config = it.config.copy(carrierFrequencyHz = value.coerceIn(80f, 400f)), error = null) }
fun setDifference(value: Float) = _ui.update { it.copy(config = it.config.copy(binauralDifferenceHz = value.coerceIn(0.5f, 20f)), error = null) }
fun updateCountdown(pref: CountdownPreference) = viewModelScope.launch { settingsRepository.updateCountdown(pref) }
fun updateCountdownSeconds(seconds: Int) = viewModelScope.launch { settingsRepository.updateCountdownSeconds(seconds) }
fun updateDefaultMode(mode: SessionMode) = viewModelScope.launch { settingsRepository.updateDefaultMode(mode) }
fun updateGuidance(value: Boolean) = viewModelScope.launch { settingsRepository.updateGuidance(value) }
fun updateShowImmersiveProgressBar(value: Boolean) = viewModelScope.launch { settingsRepository.updateShowImmersiveProgressBar(value) }
fun startSession() {
val state = _ui.value
@@ -281,7 +292,7 @@ class MainViewModel(
val clampedStartSec = startElapsedSec.coerceIn(0f, durationSec.toFloat())
if (includeCountdown) {
val count = _ui.value.settings.countdownPreference.seconds
val count = _ui.value.settings.countdownSeconds.coerceIn(0, 10)
if (count > 0) {
for (i in count downTo 1) {
_ui.update {
@@ -358,14 +369,18 @@ class MainViewModel(
return (total - state.remainingSec).coerceIn(0f, total)
}
private fun timelineForPreset(preset: SessionPreset, userPrograms: List<UserProgramEntity>): TimelineEditorState {
return userPrograms.firstOrNull { it.id == preset.id }?.timeline ?: TimelineProgramFactory.fromPreset(preset)
private fun timelineForProgramId(programId: String, programs: List<UserProgramEntity>): TimelineEditorState {
val fromSaved = programs.firstOrNull { it.id == programId }?.timeline
if (fromSaved != null) return fromSaved
val builtInPreset = Presets.builtIn.firstOrNull { it.id == programId }
return if (builtInPreset != null) TimelineProgramFactory.fromPreset(builtInPreset) else TimelineEditorState.default()
}
private fun nextSortOrder(): Int = (cachedUserPrograms.maxOfOrNull { it.sortOrder } ?: 999) + 1
private fun nextSortOrder(): Int = (cachedPrograms.maxOfOrNull { it.sortOrder } ?: 999) + 1
private fun nextUntitledName(): String {
val names = cachedUserPrograms.map { it.name.lowercase() }.toSet()
val names = cachedPrograms.map { it.name.lowercase() }.toSet()
var i = 1
while (true) {
val candidate = "Custom $i"