feat: full Compose UI (auth, calendar, events, accounts, settings, profile)
- Root navigation (server setup -> login+2FA -> calendar) with settings-driven theme - CalendarViewModel mirroring iOS CalendarStore: range-based caching, background prefetch, hidden/banished calendar filters, event CRUD - Five calendar views: Month, Week, Day, Quarter, Agenda (shared time grid) - Event detail + editor sheets (platform date/time pickers, color override, writable-calendar picker) across local/caldav/google/homeassistant - Calendar filter sheet (per-calendar show/hide) - Accounts screen: add/delete CalDAV, local, iCal, Home Assistant; list Google - Settings: view/week-start/language/colors/contrast/hour-height/cache range, synced to server - Profile: email, password change, 2FA enable/disable - gradlew build (lint + tests + assemble) passes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
package com.scarriffle.calendarr.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.scarriffle.calendarr.data.CalendarRepository
|
||||
import com.scarriffle.calendarr.data.CredentialStore
|
||||
import com.scarriffle.calendarr.data.SettingsStore
|
||||
import com.scarriffle.calendarr.data.remote.ApiProvider
|
||||
import com.scarriffle.calendarr.domain.model.AppSettings
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
enum class AppRoute { SETUP, LOGIN, MAIN }
|
||||
|
||||
@HiltViewModel
|
||||
class MainViewModel @Inject constructor(
|
||||
private val credentialStore: CredentialStore,
|
||||
private val settingsStore: SettingsStore,
|
||||
private val repository: CalendarRepository,
|
||||
private val apiProvider: ApiProvider,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _route = MutableStateFlow(computeRoute())
|
||||
val route: StateFlow<AppRoute> = _route.asStateFlow()
|
||||
|
||||
private val _settings = MutableStateFlow(settingsStore.loadSettings())
|
||||
val settings: StateFlow<AppSettings> = _settings.asStateFlow()
|
||||
|
||||
val serverUrl: String get() = credentialStore.serverUrl ?: ""
|
||||
val username: String get() = credentialStore.username ?: ""
|
||||
val isAdmin: Boolean get() = credentialStore.isAdmin
|
||||
|
||||
init {
|
||||
if (_route.value == AppRoute.MAIN) refreshSettings()
|
||||
}
|
||||
|
||||
private fun computeRoute(): AppRoute = when {
|
||||
!credentialStore.isConfigured -> AppRoute.SETUP
|
||||
!credentialStore.isLoggedIn -> AppRoute.LOGIN
|
||||
else -> AppRoute.MAIN
|
||||
}
|
||||
|
||||
fun onServerConfigured() { _route.value = computeRoute() }
|
||||
|
||||
fun onLoggedIn() {
|
||||
_route.value = AppRoute.MAIN
|
||||
refreshSettings()
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
credentialStore.clearToken()
|
||||
_route.value = computeRoute()
|
||||
}
|
||||
|
||||
fun switchServer() {
|
||||
credentialStore.clearAll()
|
||||
apiProvider.invalidate()
|
||||
_route.value = computeRoute()
|
||||
}
|
||||
|
||||
/** Pull appearance settings from the server, caching them locally. */
|
||||
fun refreshSettings() {
|
||||
viewModelScope.launch {
|
||||
runCatching { repository.getSettings() }.onSuccess { s ->
|
||||
settingsStore.saveSettings(s)
|
||||
_settings.value = s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun applyLocalSettings(s: AppSettings) {
|
||||
settingsStore.saveSettings(s)
|
||||
_settings.value = s
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user