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:
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user