Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Sourceset group by Screen #9

Merged
merged 1 commit into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 31 additions & 24 deletions app/src/commonMain/kotlin/id/gdg/app/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,25 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import id.gdg.app.di.ViewModelFactory
import id.gdg.app.ui.AppEvent
import id.gdg.app.ui.EventDetailRouter
import id.gdg.app.ui.HomeRouter
import id.gdg.app.ui.OnboardingRouter
import id.gdg.app.ui.main.MainEvent
import id.gdg.app.ui.ScreenScaffold
import id.gdg.app.ui.SplashScreenRouter
import id.gdg.app.ui.screen.EventDetailScreen
import id.gdg.app.ui.screen.MainScreen
import id.gdg.app.ui.screen.OnboardingScreen
import id.gdg.app.ui.screen.SplashScreen
import id.gdg.app.ui.detail.EventDetailRouter
import id.gdg.app.ui.detail.EventDetailScreen
import id.gdg.app.ui.detail.EventDetailViewModel
import id.gdg.app.ui.main.MainRouter
import id.gdg.app.ui.main.MainScreen
import id.gdg.app.ui.main.MainViewModel
import id.gdg.app.ui.onboarding.OnboardingRouter
import id.gdg.app.ui.onboarding.OnboardingScreen
import id.gdg.app.ui.onboarding.OnboardingViewModel
import id.gdg.app.ui.splash.SplashScreen
import id.gdg.app.ui.splash.SplashScreenRouter

@Composable
fun AppContent(
viewModel: AppViewModel = ViewModelFactory.create(),
onboardingViewModel: OnboardingViewModel = ViewModelFactory.onboardingViewModel(),
mainViewModel: MainViewModel = ViewModelFactory.mainViewModel(),
eventDetailViewModel: EventDetailViewModel = ViewModelFactory.eventDetailViewModel(),
navController: NavHostController = rememberNavController()
) {
Scaffold { innerPadding ->
Expand All @@ -47,12 +52,9 @@ fun AppContent(

composable<OnboardingRouter> {
OnboardingScreen(
chapterList = viewModel.chapterList,
onChapterSelected = { chapterId ->
viewModel.sendEvent(AppEvent.ChangeChapterId(chapterId))
},
viewModel = onboardingViewModel,
navigateToMainScreen = {
navController.navigate(HomeRouter) {
navController.navigate(MainRouter) {
popUpTo(OnboardingRouter) {
inclusive = true
}
Expand All @@ -61,16 +63,15 @@ fun AppContent(
)
}

composable<HomeRouter> {
composable<MainRouter> {
ScreenScaffold(
viewModel = viewModel,
mainScreen = { viewModel, onEventDetailClicked ->
mainScreen = { onEventDetailClicked ->
MainScreen(
viewModel = viewModel,
viewModel = mainViewModel,
onEventDetailClicked = onEventDetailClicked
)
},
detailScreen = { viewModel, eventId ->
detailScreen = { eventId ->
/**
* Need to show the detail screen on same composable screen
* due to side-to-side scaffold for tablet nor large screens.
Expand All @@ -80,8 +81,11 @@ fun AppContent(
* due to side-to-side scaffold for tablet nor large screens.
*/
EventDetailScreen(
viewModel = viewModel,
eventId = eventId
viewModel = eventDetailViewModel,
eventId = eventId,
onBack = {

}
)
},
navigateToDetailScreen = { eventId ->
Expand All @@ -94,8 +98,11 @@ fun AppContent(
val eventDetail = it.toRoute<EventDetailRouter>()

EventDetailScreen(
viewModel = viewModel,
eventId = eventDetail.eventId
viewModel = eventDetailViewModel,
eventId = eventDetail.eventId,
onBack = {

}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package id.gdg.app.ui.state.common
package id.gdg.app.common

sealed class UiState {
data object Success : UiState()
Expand Down
4 changes: 2 additions & 2 deletions app/src/commonMain/kotlin/id/gdg/app/data/BottomNavBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import id.gdg.app.androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope
import kotlin.jvm.JvmInline

val HomepageMenu = SelectedId(0)
val MainMenu = SelectedId(0)
val ChapterInfoMenu = SelectedId(1)
val ProfileMenu = SelectedId(2)

Expand All @@ -32,7 +32,7 @@ object BottomNavBar {
}

private fun create() = listOf(
NavItem(HomepageMenu, "Home", Icons.Filled.Home),
NavItem(MainMenu, "Home", Icons.Filled.Home),
NavItem(ChapterInfoMenu,"Chapter", Icons.Filled.Info),
NavItem(ProfileMenu,"Profile", Icons.Filled.Person),
)
Expand Down
22 changes: 17 additions & 5 deletions app/src/commonMain/kotlin/id/gdg/app/di/ViewModelFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package id.gdg.app.di

import androidx.compose.runtime.Composable
import androidx.lifecycle.viewmodel.compose.viewModel
import id.gdg.app.AppViewModel
import id.gdg.app.ui.main.MainViewModel
import id.gdg.app.ui.detail.EventDetailViewModel
import id.gdg.app.ui.onboarding.OnboardingViewModel
import id.gdg.chapter.domain.GetChapterIdUseCase
import id.gdg.chapter.domain.GetChapterListUseCase
import id.gdg.chapter.domain.SetChapterIdUseCase
Expand All @@ -25,14 +27,24 @@ object ViewModelFactory : KoinComponent {
private val eventDetailUseCase: GetEventDetailUseCase by inject()

@Composable
fun create() = viewModel {
AppViewModel(
fun onboardingViewModel() = viewModel {
OnboardingViewModel(
chapterListUseCase,
setCurrentChapterUseCase
)
}

@Composable
fun mainViewModel() = viewModel {
MainViewModel(
getCurrentChapterUseCase,
setCurrentChapterUseCase,
upComingEventUseCase,
previousEventUseCase,
eventDetailUseCase
previousEventUseCase
)
}
@Composable
fun eventDetailViewModel() = viewModel {
EventDetailViewModel(eventDetailUseCase)
}
}
12 changes: 0 additions & 12 deletions app/src/commonMain/kotlin/id/gdg/app/ui/AppEvent.kt

This file was deleted.

16 changes: 1 addition & 15 deletions app/src/commonMain/kotlin/id/gdg/app/ui/Router.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
package id.gdg.app.ui

import kotlinx.serialization.Serializable

sealed interface Router

@Serializable
data object SplashScreenRouter : Router

@Serializable
data object OnboardingRouter : Router

@Serializable
data object HomeRouter : Router

@Serializable
data class EventDetailRouter(val eventId: String) : Router
interface Router
17 changes: 8 additions & 9 deletions app/src/commonMain/kotlin/id/gdg/app/ui/ScreenScaffold.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,24 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.dp
import id.gdg.app.AppViewModel
import id.gdg.app.ui.main.MainViewModel
import id.gdg.app.androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import id.gdg.app.data.BottomNavBar
import id.gdg.app.data.ChapterInfoMenu
import id.gdg.app.data.HomepageMenu
import id.gdg.app.data.MainMenu
import id.gdg.app.data.ProfileMenu
import id.gdg.ui.LocalWindowSizeClass
import id.gdg.ui.TwoPanelScaffold
import id.gdg.ui.TwoPanelScaffoldAnimationSpec

@Composable
fun ScreenScaffold(
viewModel: AppViewModel,
mainScreen: @Composable (AppViewModel, onEventDetailClicked: (String) -> Unit) -> Unit,
detailScreen: @Composable (AppViewModel, String) -> Unit,
mainScreen: @Composable (onEventDetailClicked: (String) -> Unit) -> Unit,
detailScreen: @Composable (String) -> Unit,
navigateToDetailScreen: (String) -> Unit
) {
var selectedEventId by rememberSaveable { mutableStateOf("") }
var selectedBottomNavItem by rememberSaveable { mutableStateOf(HomepageMenu) }
var selectedBottomNavItem by rememberSaveable { mutableStateOf(MainMenu) }

val windowSizeClazz = LocalWindowSizeClass.current
var shouldPanelOpened: Boolean? by rememberSaveable { mutableStateOf(null) }
Expand All @@ -54,13 +53,13 @@ fun ScreenScaffold(
) {
Surface {
when (selectedBottomNavItem) {
HomepageMenu -> TwoPanelScaffold(
MainMenu -> TwoPanelScaffold(
panelVisibility = panelVisibility,
animationSpec = TwoPanelScaffoldAnimationSpec(
finishedListener = { fraction -> if (fraction == 1f) shouldPanelOpened = null }
),
body = {
mainScreen(viewModel) {
mainScreen {
// If the screen size is compact (or mobile device screen size), then
// navigate to detail page with router. Otherwise, render the [panel].
if (windowSizeClazz.widthSizeClass == WindowWidthSizeClass.Compact) {
Expand All @@ -76,7 +75,7 @@ fun ScreenScaffold(
panel = {
Surface(tonalElevation = 1.dp) {
if (shouldPanelOpened != null) {
detailScreen(viewModel, selectedEventId)
detailScreen(selectedEventId)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package id.gdg.app.ui.detail

import id.gdg.app.ui.Router
import kotlinx.serialization.Serializable

@Serializable
data class EventDetailRouter(val eventId: String) : Router
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package id.gdg.app.ui.screen
package id.gdg.app.ui.detail

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Box
Expand All @@ -10,16 +10,22 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import id.gdg.app.AppViewModel
import id.gdg.app.ui.AppEvent

@Composable
fun EventDetailScreen(viewModel: AppViewModel, eventId: String) {
fun EventDetailScreen(
viewModel: EventDetailViewModel,
eventId: String,
onBack: () -> Unit
) {
val eventDetailUiState by viewModel.eventDetailUiState.collectAsState()

LaunchedEffect(eventId) {
if (eventId.isEmpty()) return@LaunchedEffect
viewModel.sendEvent(AppEvent.EventDetail(eventId.toInt()))
if (eventId.isEmpty()) {
onBack()
return@LaunchedEffect
}

viewModel.fetch(eventId.toInt())
}

Box {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package id.gdg.app.ui.state
package id.gdg.app.ui.detail

import id.gdg.app.ui.state.common.UiState
import id.gdg.app.common.UiState
import id.gdg.event.model.EventDetailModel

data class EventDetailUiModel(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package id.gdg.app.ui.detail

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import id.gdg.app.common.UiState
import id.gdg.app.common.asUiState
import id.gdg.event.domain.GetEventDetailUseCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class EventDetailViewModel(
private val eventDetailUseCase: GetEventDetailUseCase
) : ViewModel() {

private var _eventDetailUiState = MutableStateFlow(EventDetailUiModel.Empty)
val eventDetailUiState get() = _eventDetailUiState.asStateFlow()

fun fetch(eventId: Int) {
_eventDetailUiState.update { it.copy(state = UiState.Loading) }

viewModelScope.launch {
val result = eventDetailUseCase(eventId)

withContext(Dispatchers.Main) {
_eventDetailUiState.update {
it.copy(
state = result.asUiState(),
detail = result.getOrNull()
)
}
}
}
}
}
11 changes: 11 additions & 0 deletions app/src/commonMain/kotlin/id/gdg/app/ui/main/MainEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package id.gdg.app.ui.main

sealed class MainEvent {

data class ChangeChapterId(val chapterId: Int) : MainEvent()

data object InitialContent : MainEvent()

data object FetchPreviousEvent : MainEvent()
data object FetchUpcomingEvent : MainEvent()
}
7 changes: 7 additions & 0 deletions app/src/commonMain/kotlin/id/gdg/app/ui/main/MainRouter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package id.gdg.app.ui.main

import id.gdg.app.ui.Router
import kotlinx.serialization.Serializable

@Serializable
data object MainRouter : Router
Loading
Loading