perf: unified splash held until loaded, smoother month scrolling, serialized loads

- Single system splash (core-splashscreen) kept on screen until the first
  events load (StartupState), then reveals the ready app — removes the
  two-stage 'icon then title' splash and the laggy half-loaded entry
- Month scrolling: dropped per-row BoxWithConstraints (measured grid width once
  via onSizeChanged) and resolve bar colours once during packing instead of
  every recomposition → much smoother scroll
- Serialize all event loads behind a Mutex and skip redundant state writes so
  scroll-triggered month loads no longer pile up / thrash the UI

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Guido Schmit
2026-05-31 14:53:06 +02:00
parent 3734e17c3f
commit ba7daf8559
6 changed files with 112 additions and 130 deletions

View File

@@ -4,16 +4,35 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.lifecycleScope
import com.scarriffle.calendarr.data.CredentialStore
import com.scarriffle.calendarr.data.StartupState
import com.scarriffle.calendarr.ui.CalendarrRoot
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject lateinit var startupState: StartupState
@Inject lateinit var credentialStore: CredentialStore
override fun onCreate(savedInstanceState: Bundle?) {
// Covers the window from the first frame (no warm-start flash), then
// hands off to the in-app splash which stays until data is loaded.
installSplashScreen()
// Single, uniform splash: keep the system splash on screen until the
// first events have loaded (or a timeout), so there is no two-stage
// splash and no entering a half-loaded, janky app.
val splash = installSplashScreen()
splash.setKeepOnScreenCondition { !startupState.ready.value }
super.onCreate(savedInstanceState)
// Nothing to load before login → reveal immediately.
if (!credentialStore.isLoggedIn) startupState.markReady()
// Safety timeout so the splash can never hang.
lifecycleScope.launch { delay(6000); startupState.markReady() }
setContent {
CalendarrRoot()
}