feat: wire up app root, navigation, and platform entry points

- Add App.kt root composable with state-based routing
  (Login -> Schedule -> EventDetail) driven by AuthViewModel.state
- Update MainActivity (Android), MainViewController (iOS), and
  Main.kt (Desktop) to host App() with their platform OAuthClient
- Add iOS Xcode source files (MyIceApp.swift, ContentView.swift)
  and Info.plist with myice:// URL scheme
- Delete Placeholder.kt
- Add kotlinx-coroutines-swing to desktopMain to provide
  Dispatchers.Main for viewModelScope (app crashed on desktop
  without it)
This commit is contained in:
2026-07-08 18:00:57 +02:00
parent 88aa9de4ad
commit 28de93263b
10 changed files with 161 additions and 7 deletions
+1
View File
@@ -59,6 +59,7 @@ kotlin {
val desktopMain by getting {
dependencies {
implementation(libs.ktor.client.cio)
implementation(libs.kotlinx.coroutines.swing)
implementation(compose.desktop.currentOs)
}
}
@@ -4,14 +4,14 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.Text
import ch.parano.myice.auth.AndroidOAuthClient
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
setContent {
Text("MyIce")
App(oauthClient = AndroidOAuthClient(applicationContext))
}
}
}
@@ -0,0 +1,77 @@
package ch.parano.myice
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import ch.parano.myice.auth.AuthSettings
import ch.parano.myice.auth.OAuthClient
import ch.parano.myice.cache.ScheduleCache
import ch.parano.myice.network.ApiService
import ch.parano.myice.network.createHttpClient
import ch.parano.myice.ui.screens.EventDetailScreen
import ch.parano.myice.ui.screens.LoginScreen
import ch.parano.myice.ui.screens.ScheduleScreen
import ch.parano.myice.ui.theme.MyIceTheme
import ch.parano.myice.viewmodel.AuthState
import ch.parano.myice.viewmodel.AuthViewModel
import ch.parano.myice.viewmodel.EventDetailViewModel
import ch.parano.myice.viewmodel.ScheduleViewModel
import com.russhwolf.settings.Settings
sealed class Screen {
data object Login : Screen()
data object Schedule : Screen()
data class EventDetail(val gameId: String, val account: String, val eventTitle: String) : Screen()
}
@Composable
fun App(
oauthClient: OAuthClient,
) {
val apiService = remember { ApiService(createHttpClient()) }
val authSettings = remember { AuthSettings(Settings()) }
val cache = remember { ScheduleCache(Settings()) }
val authViewModel = remember { AuthViewModel(apiService, authSettings, oauthClient) }
val scheduleViewModel = remember { ScheduleViewModel(apiService, cache, authSettings) }
val eventDetailViewModel = remember { EventDetailViewModel(apiService) }
var currentScreen by remember { mutableStateOf<Screen>(Screen.Login) }
LaunchedEffect(Unit) {
authViewModel.init()
}
val authState by authViewModel.state.collectAsState()
LaunchedEffect(authState) {
currentScreen = when (authState) {
is AuthState.Authenticated -> Screen.Schedule
is AuthState.Unauthenticated -> Screen.Login
AuthState.Loading -> currentScreen
}
}
MyIceTheme {
when (val screen = currentScreen) {
Screen.Login -> LoginScreen(viewModel = authViewModel)
Screen.Schedule -> ScheduleScreen(
scheduleViewModel = scheduleViewModel,
authViewModel = authViewModel,
onEventClick = { gameId, account, eventTitle ->
currentScreen = Screen.EventDetail(gameId, account, eventTitle)
},
)
is Screen.EventDetail -> EventDetailScreen(
gameId = screen.gameId,
account = screen.account,
eventTitle = screen.eventTitle,
viewModel = eventDetailViewModel,
)
}
}
}
@@ -1 +0,0 @@
package ch.parano.myice
@@ -1,11 +1,11 @@
package ch.parano.myice
import androidx.compose.material3.Text
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import ch.parano.myice.auth.DesktopOAuthClient
fun main() = application {
Window(onCloseRequest = ::exitApplication, title = "MyIce") {
Text("MyIce")
App(oauthClient = DesktopOAuthClient())
}
}
@@ -1,8 +1,8 @@
package ch.parano.myice
import androidx.compose.material3.Text
import androidx.compose.ui.window.ComposeUIViewController
import ch.parano.myice.auth.IosOAuthClient
fun MainViewController() = ComposeUIViewController {
Text("MyIce")
App(oauthClient = IosOAuthClient())
}