diff --git a/core/designsystem/src/main/java/in/koreatech/koin/core/designsystem/component/chip/TextChip.kt b/core/designsystem/src/main/java/in/koreatech/koin/core/designsystem/component/chip/TextChip.kt index 47e80d290..639861acb 100644 --- a/core/designsystem/src/main/java/in/koreatech/koin/core/designsystem/component/chip/TextChip.kt +++ b/core/designsystem/src/main/java/in/koreatech/koin/core/designsystem/component/chip/TextChip.kt @@ -42,15 +42,15 @@ fun TextChip( Box( modifier = modifier .clip(shape) - .background(if(isSelected) chipColors.selectedContainerColor else chipColors.unselectedContainerColor) - .padding(contentPadding) .then( if (showClickRipple) Modifier.clickable { onSelect() } else Modifier.noRippleClickable { onSelect() } - ), + ) + .background(if(isSelected) chipColors.selectedContainerColor else chipColors.unselectedContainerColor) + .padding(contentPadding), contentAlignment = Alignment.Center ) { Text( diff --git a/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingManager.kt b/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingManager.kt index 5ef18fef8..625410e6a 100644 --- a/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingManager.kt +++ b/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingManager.kt @@ -99,6 +99,23 @@ class OnboardingManager @Inject internal constructor( } } + fun getShouldOnboardFlow( + type: OnboardingType + ) = onboardingRepository.getShouldOnboardingFlow(type.name) + + suspend fun getShouldOnboard( + type: OnboardingType, + ) : Boolean { + return onboardingRepository.getShouldOnboarding(type.name) + } + + suspend fun updateShouldOnboard( + type: OnboardingType, + shouldShow: Boolean + ) { + onboardingRepository.updateShouldOnboarding(type.name, shouldShow) + } + fun dismissTooltip() { if (::tooltip.isInitialized) tooltip.dismiss() diff --git a/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingModule.kt b/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingModule.kt index 63573dced..fc7d864f1 100644 --- a/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingModule.kt +++ b/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingModule.kt @@ -7,14 +7,16 @@ import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityComponent import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ActivityScoped +import dagger.hilt.components.SingletonComponent import `in`.koreatech.koin.domain.repository.OnboardingRepository +import javax.inject.Singleton @Module -@InstallIn(ActivityComponent::class) +@InstallIn(SingletonComponent::class) object OnboardingModule { @Provides - @ActivityScoped + @Singleton fun provideOnboardingManager( onboardingRepository: OnboardingRepository, @ApplicationContext context: Context diff --git a/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingType.kt b/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingType.kt index 0dad8b6e7..c6d8746aa 100644 --- a/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingType.kt +++ b/core/onboarding/src/main/java/in/koreatech/koin/core/onboarding/OnboardingType.kt @@ -12,5 +12,6 @@ enum class OnboardingType( DINING_IMAGE(R.string.dining_image_tooltip), DINING_NOTIFICATION(0), DINING_SHARE(0), - ARTICLE_KEYWORD(R.string.article_keyword_tooltip) + ARTICLE_KEYWORD(R.string.article_keyword_tooltip), + SHOW_BUS_HEAD_ARTICLE(0) } \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/di/onboarding/OnboardingModule.kt b/data/src/main/java/in/koreatech/koin/data/di/onboarding/OnboardingModule.kt index 07472994b..25346c3ef 100644 --- a/data/src/main/java/in/koreatech/koin/data/di/onboarding/OnboardingModule.kt +++ b/data/src/main/java/in/koreatech/koin/data/di/onboarding/OnboardingModule.kt @@ -11,23 +11,25 @@ import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityComponent import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ActivityScoped +import dagger.hilt.components.SingletonComponent import `in`.koreatech.koin.data.repository.OnboardingRepositoryImpl import `in`.koreatech.koin.data.source.local.OnboardingLocalDataSource import `in`.koreatech.koin.domain.repository.OnboardingRepository +import javax.inject.Singleton @Module -@InstallIn(ActivityComponent::class) +@InstallIn(SingletonComponent::class) abstract class OnboardingRepositoryModule { @Binds - @ActivityScoped + @Singleton abstract fun bindOnboardingRepository( onboardingRepositoryImpl: OnboardingRepositoryImpl ): OnboardingRepository } @Module -@InstallIn(ActivityComponent::class) +@InstallIn(SingletonComponent::class) object OnboardingLocalDataSourceModule { private val Context.onboardingDataStore: DataStore by preferencesDataStore( @@ -35,7 +37,7 @@ object OnboardingLocalDataSourceModule { ) @Provides - @ActivityScoped + @Singleton fun provideOnboardingManager( @ApplicationContext context: Context ): OnboardingLocalDataSource { diff --git a/data/src/main/java/in/koreatech/koin/data/repository/OnboardingRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/OnboardingRepositoryImpl.kt index 5bfbe2552..3f2314492 100644 --- a/data/src/main/java/in/koreatech/koin/data/repository/OnboardingRepositoryImpl.kt +++ b/data/src/main/java/in/koreatech/koin/data/repository/OnboardingRepositoryImpl.kt @@ -2,6 +2,7 @@ package `in`.koreatech.koin.data.repository import `in`.koreatech.koin.data.source.local.OnboardingLocalDataSource import `in`.koreatech.koin.domain.repository.OnboardingRepository +import kotlinx.coroutines.flow.Flow import javax.inject.Inject class OnboardingRepositoryImpl @Inject constructor( @@ -15,4 +16,7 @@ class OnboardingRepositoryImpl @Inject constructor( override suspend fun updateShouldOnboarding(onboardingType: String, shouldShow: Boolean) { onboardingLocalDataSource.updateShouldOnboarding(onboardingType, shouldShow) } + + override fun getShouldOnboardingFlow(onboardingType: String): Flow = + onboardingLocalDataSource.getShouldOnboardingFlow(onboardingType) } \ No newline at end of file diff --git a/data/src/main/java/in/koreatech/koin/data/source/local/OnboardingLocalDataSource.kt b/data/src/main/java/in/koreatech/koin/data/source/local/OnboardingLocalDataSource.kt index 647db6a93..3729b43dc 100644 --- a/data/src/main/java/in/koreatech/koin/data/source/local/OnboardingLocalDataSource.kt +++ b/data/src/main/java/in/koreatech/koin/data/source/local/OnboardingLocalDataSource.kt @@ -27,4 +27,10 @@ class OnboardingLocalDataSource @Inject constructor( preferences[booleanPreferencesKey(onboardingType)] = shouldShow } } + + fun getShouldOnboardingFlow(onboardingType: String) = dataStore.data.catch { + emit(emptyPreferences()) + }.map { preferences -> + preferences[booleanPreferencesKey(onboardingType)] ?: true + } } \ No newline at end of file diff --git a/domain/src/main/java/in/koreatech/koin/domain/repository/OnboardingRepository.kt b/domain/src/main/java/in/koreatech/koin/domain/repository/OnboardingRepository.kt index 689070842..b3629f836 100644 --- a/domain/src/main/java/in/koreatech/koin/domain/repository/OnboardingRepository.kt +++ b/domain/src/main/java/in/koreatech/koin/domain/repository/OnboardingRepository.kt @@ -1,6 +1,9 @@ package `in`.koreatech.koin.domain.repository +import kotlinx.coroutines.flow.Flow + interface OnboardingRepository { suspend fun getShouldOnboarding(onboardingType: String): Boolean suspend fun updateShouldOnboarding(onboardingType: String, shouldShow: Boolean) + fun getShouldOnboardingFlow(onboardingType: String): Flow } \ No newline at end of file diff --git a/feature/bus/build.gradle.kts b/feature/bus/build.gradle.kts index bac9fe443..1ad02fcdf 100644 --- a/feature/bus/build.gradle.kts +++ b/feature/bus/build.gradle.kts @@ -3,6 +3,7 @@ plugins { alias(libs.plugins.koin.hilt) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlinx.serialization) + id("kotlin-parcelize") } android { @@ -20,6 +21,7 @@ android { dependencies { implementation(project(":core")) implementation(project(":domain")) + implementation(project(":core:onboarding")) implementation(project(":core:designsystem")) implementation(libs.core.ktx) diff --git a/feature/bus/src/main/java/in/koreatech/bus/Bus2Activity.kt b/feature/bus/src/main/java/in/koreatech/bus/Bus2Activity.kt index cb9330110..127541ddc 100644 --- a/feature/bus/src/main/java/in/koreatech/bus/Bus2Activity.kt +++ b/feature/bus/src/main/java/in/koreatech/bus/Bus2Activity.kt @@ -1,6 +1,7 @@ package `in`.koreatech.bus import android.os.Bundle +import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.fillMaxSize @@ -13,6 +14,7 @@ import androidx.core.view.WindowInsetsCompat import androidx.navigation.compose.rememberNavController import dagger.hilt.android.AndroidEntryPoint import `in`.koreatech.bus.navigation.BusNavigation +import `in`.koreatech.koin.core.designsystem.theme.KoinTheme import `in`.koreatech.koin.feature.bus.R @AndroidEntryPoint @@ -20,12 +22,10 @@ class Bus2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_bus2) - findViewById(R.id.compose_view_bus).setContent { - MaterialTheme { + setContent { + KoinTheme { BusNavigation( - modifier = Modifier.fillMaxSize(), - navController = rememberNavController(), + modifier = Modifier.fillMaxSize() ) } } diff --git a/feature/bus/src/main/java/in/koreatech/bus/navigation/BusNavigation.kt b/feature/bus/src/main/java/in/koreatech/bus/navigation/BusNavigation.kt index 00341c717..15f7eeea2 100644 --- a/feature/bus/src/main/java/in/koreatech/bus/navigation/BusNavigation.kt +++ b/feature/bus/src/main/java/in/koreatech/bus/navigation/BusNavigation.kt @@ -2,20 +2,19 @@ package `in`.koreatech.bus.navigation import androidx.compose.animation.EnterTransition import androidx.compose.animation.ExitTransition +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import `in`.koreatech.bus.screen.timetable.viewmodel.BusViewModel +import `in`.koreatech.bus.screen.timetable.composable.BusTimetableScreen @Composable fun BusNavigation( modifier: Modifier = Modifier, navController: NavHostController = rememberNavController(), - viewModel: BusViewModel = hiltViewModel() ) { NavHost( @@ -30,7 +29,10 @@ fun BusNavigation( ) { composable { - + BusTimetableScreen( + modifier = Modifier.fillMaxSize(), + onNavigationIconClick = { navController.popBackStack() } + ) } } } \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/BusTimetableScreen.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/BusTimetableScreen.kt index 44d87e7bc..d831dfee9 100644 --- a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/BusTimetableScreen.kt +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/BusTimetableScreen.kt @@ -3,48 +3,67 @@ package `in`.koreatech.bus.screen.timetable.composable import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Close import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import `in`.koreatech.bus.screen.timetable.type.BusType -import `in`.koreatech.bus.screen.timetable.type.ShuttleBusRouteType import `in`.koreatech.bus.screen.timetable.viewmodel.BusTimetableViewModel -import `in`.koreatech.bus.viewstate.ShuttleRegionViewState -import `in`.koreatech.bus.viewstate.ShuttleTimetableOverviewViewState -import `in`.koreatech.koin.core.designsystem.component.chip.TextChipGroup import `in`.koreatech.koin.core.designsystem.component.tab.KoinTabRow import `in`.koreatech.koin.core.designsystem.component.text.LeadingIconText import `in`.koreatech.koin.core.designsystem.component.topbar.KoinTopAppBar +import `in`.koreatech.koin.core.designsystem.noRippleClickable import `in`.koreatech.koin.core.designsystem.theme.KoinTheme import `in`.koreatech.koin.feature.bus.R -import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable internal fun BusTimetableScreen( modifier: Modifier = Modifier, onNavigationIconClick: () -> Unit = {}, - viewModel: BusTimetableViewModel = hiltViewModel() + viewModel: BusTimetableViewModel = hiltViewModel(), + previewTab: BusType = BusType.SHUTTLE ) { - var selectedTimetableTypeTabIndex by rememberSaveable { mutableIntStateOf(0) } + var selectedTimetableTypeTab by rememberSaveable { mutableStateOf(BusType.SHUTTLE) } + val busTypeHeadTitle = when(selectedTimetableTypeTab) { + BusType.SHUTTLE -> stringResource(R.string.shuttle_timetable) + BusType.EXPRESS -> stringResource(R.string.express_timetable) + BusType.CITY -> stringResource(R.string.city_timetable) + } + + val shuttleRegions by viewModel.shuttleRegions.collectAsStateWithLifecycle() + val expressTimetable by viewModel.expressTimetable.collectAsStateWithLifecycle() + val cityTimetable by viewModel.cityTimetable.collectAsStateWithLifecycle() + + val shouldShowNotice by viewModel.shouldShowNotice.collectAsStateWithLifecycle() + val notice by viewModel.notice.collectAsStateWithLifecycle() Column( modifier = modifier @@ -57,10 +76,10 @@ internal fun BusTimetableScreen( LazyColumn { item { Column( - modifier = Modifier.fillMaxWidth().background(Color.White).padding(start = 24.dp) + modifier = Modifier.fillMaxWidth().background(Color.White).padding(horizontal = 24.dp) ) { Text( - text = stringResource(R.string.shuttle_timetable), + text = busTypeHeadTitle, style = KoinTheme.typography.bold20 ) Spacer(modifier = Modifier.height(8.dp)) @@ -68,77 +87,67 @@ internal fun BusTimetableScreen( text = stringResource(R.string.request_for_incorrect_information), iconRes = R.drawable.ic_caution ) + if (shouldShowNotice || LocalInspectionMode.current) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .background( + color = KoinTheme.colors.info100, + shape = RoundedCornerShape(8.dp) + ).padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + modifier = Modifier.weight(1f), + text = notice, + style = KoinTheme.typography.medium14, + color = KoinTheme.colors.primary500, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Icon( + modifier = Modifier.padding(start = 4.dp).size(16.dp).noRippleClickable { + viewModel.closeNotice() + }, + imageVector = Icons.Rounded.Close, + contentDescription = notice, + tint = KoinTheme.colors.neutral300 + ) + } + } } } stickyHeader { KoinTabRow( titles = BusType.entries.map { stringResource(it.titleRes) }, - selectedTabIndex = selectedTimetableTypeTabIndex, - onTabSelected = { selectedTimetableTypeTabIndex = it } + selectedTabIndex = selectedTimetableTypeTab.ordinal, + onTabSelected = { selectedTimetableTypeTab = BusType.entries[it] } ) } item { - when(selectedTimetableTypeTabIndex) { - BusType.SHUTTLE.ordinal -> { + if (LocalInspectionMode.current) + selectedTimetableTypeTab = previewTab + + when(selectedTimetableTypeTab) { + BusType.SHUTTLE -> { ShuttleTimetableScreen( modifier = Modifier.fillMaxSize().background(KoinTheme.colors.neutral100), - regions = persistentListOf( - ShuttleRegionViewState( - name = "서울", - timetableOverviews = listOf( - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.WEEKDAY, - name = "서울-대전", - ), - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.WEEKEND, - name = "서울-대전", - ), - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.CIRCULATION, - name = "서울-대전", - ) - ) - ), - ShuttleRegionViewState( - name = "대전", - timetableOverviews = listOf( - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.WEEKDAY, - name = "대전-서울", - ), - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.WEEKEND, - name = "대전-서울", - description = "대전에서 서울로 이동하는 노선입니다." - ), - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.CIRCULATION, - name = "대전-서울", - description = "대전에서 서울로 이동하는 노선입니다." - ) - ) - ), - ShuttleRegionViewState( - name = "대구", - timetableOverviews = listOf( - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.WEEKDAY, - name = "대구-서울", - ), - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.WEEKDAY, - name = "대구-서울", - ), - ShuttleTimetableOverviewViewState( - routeType = ShuttleBusRouteType.WEEKEND, - name = "대구-서울", - ) - ) - ) - ) + regions = shuttleRegions.toPersistentList() + ) + } + BusType.EXPRESS -> { + ExpressTimetableScreen( + modifier = Modifier.fillMaxSize().background(KoinTheme.colors.neutral100), + timetable = expressTimetable + ) + } + BusType.CITY -> { + CityTimetableScreen( + modifier = Modifier.fillMaxSize().background(KoinTheme.colors.neutral100), + timetable = cityTimetable ) } } @@ -150,8 +159,25 @@ internal fun BusTimetableScreen( @Preview(showBackground = true) @Composable -private fun BusTimetableScreenPreview() { +private fun BusTimetableShuttleScreenPreview() { + BusTimetableScreen( + modifier = Modifier.fillMaxSize(), + previewTab = BusType.SHUTTLE + ) +} +@Preview(showBackground = true) +@Composable +private fun BusTimetableExpressScreenPreview() { + BusTimetableScreen( + modifier = Modifier.fillMaxSize(), + previewTab = BusType.EXPRESS + ) +} +@Preview(showBackground = true) +@Composable +private fun BusTimetableCityScreenPreview() { BusTimetableScreen( - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), + previewTab = BusType.CITY ) } \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CityTimetableScreen.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CityTimetableScreen.kt new file mode 100644 index 000000000..eb076468e --- /dev/null +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CityTimetableScreen.kt @@ -0,0 +1,135 @@ +package `in`.koreatech.bus.screen.timetable.composable + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import `in`.koreatech.bus.screen.timetable.type.CityBusNumberType +import `in`.koreatech.bus.screen.timetable.type.CommonDirectionType +import `in`.koreatech.bus.screen.timetable.type.DaytimeType +import `in`.koreatech.bus.viewstate.ArrivalViewState +import `in`.koreatech.bus.viewstate.CommonTimetableViewState +import `in`.koreatech.koin.core.designsystem.component.chip.TextChipGroup +import `in`.koreatech.koin.core.designsystem.theme.KoinTheme +import `in`.koreatech.koin.feature.bus.R + +@Composable +internal fun CityTimetableScreen( + timetable: CommonTimetableViewState, + modifier: Modifier = Modifier, + onBusNumberChanged: (CityBusNumberType) -> Unit = {}, + onDirectionChanged: (CommonDirectionType) -> Unit = {} +) { + + var selectedBusNumberType by rememberSaveable { mutableStateOf(CityBusNumberType.N400) } + var selectedDirectionType by rememberSaveable { mutableStateOf(CommonDirectionType.TO_BYEONGCHEON) } + val context = LocalContext.current + + Column( + modifier = modifier + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(top = 8.dp, bottom = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(R.string.routes), + style = KoinTheme.typography.regular16, + color = KoinTheme.colors.neutral600 + ) + TextChipGroup( + modifier = Modifier.padding(start = 16.dp), + titles = CityBusNumberType.entries.map { stringResource(it.titleRes) }, + onChipSelected = { title -> + selectedBusNumberType = + CityBusNumberType.entries.find { context.getString(it.titleRes) == title } ?: CityBusNumberType.N400 + }, + selectedChipIndexes = intArrayOf(selectedBusNumberType.ordinal) + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(top = 4.dp, bottom = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(R.string.operating), + style = KoinTheme.typography.regular16, + color = KoinTheme.colors.neutral600 + ) + TextChipGroup( + modifier = Modifier.padding(start = 16.dp), + titles = CommonDirectionType.entries.map { stringResource(it.titleRes) }, + onChipSelected = { title -> + selectedDirectionType = + CommonDirectionType.entries.find { context.getString(it.titleRes) == title } ?: CommonDirectionType.TO_BYEONGCHEON + }, + selectedChipIndexes = intArrayOf(selectedDirectionType.ordinal) + ) + } + + CommonTimetableView( + timetable = timetable, + modifier = Modifier.fillMaxSize() + ) + } + + LaunchedEffect(selectedBusNumberType) { + onBusNumberChanged(selectedBusNumberType) + } + + LaunchedEffect(selectedDirectionType) { + onDirectionChanged(selectedDirectionType) + } +} + +@Preview +@Composable +private fun CityTimetableScreenPreview() { + CityTimetableScreen( + modifier = Modifier.fillMaxSize().background(KoinTheme.colors.neutral100), + timetable = CommonTimetableViewState( + updatedAt = "2024-09-21", + arrivals = mapOf( + DaytimeType.AM to listOf( + ArrivalViewState( + arrivalTime = "09:00" + ), + ArrivalViewState( + arrivalTime = "09:30" + ), + ArrivalViewState( + arrivalTime = "10:00" + ), + ArrivalViewState( + arrivalTime = "10:30" + ), + ), DaytimeType.PM to listOf( + ArrivalViewState( + arrivalTime = "14:30" + ) + ) + ) + ) + ) +} \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CommonTimetableItem.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CommonTimetableItem.kt new file mode 100644 index 000000000..fc5a1cbb9 --- /dev/null +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CommonTimetableItem.kt @@ -0,0 +1,47 @@ +package `in`.koreatech.bus.screen.timetable.composable + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import `in`.koreatech.bus.viewstate.ArrivalViewState +import `in`.koreatech.koin.core.designsystem.theme.KoinTheme + +@Composable +internal fun CommonTimetableItem( + arrival: ArrivalViewState, + textStyle: TextStyle, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + ) { + Text( + modifier = Modifier.padding(vertical = 16.dp), + text = arrival.arrivalTime, + style = textStyle, + ) + HorizontalDivider( + color = KoinTheme.colors.neutral200, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun CommonTimetableItemPreview() { + CommonTimetableItem( + arrival = ArrivalViewState( + arrivalTime = "09:00" + ), + textStyle = KoinTheme.typography.bold18.copy( + color = KoinTheme.colors.warning500 + ), + modifier = Modifier.padding(vertical = 12.dp) + ) +} \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CommonTimetableView.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CommonTimetableView.kt new file mode 100644 index 000000000..b2d8288e8 --- /dev/null +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/CommonTimetableView.kt @@ -0,0 +1,127 @@ +package `in`.koreatech.bus.screen.timetable.composable + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import `in`.koreatech.bus.screen.timetable.type.DaytimeType +import `in`.koreatech.bus.viewstate.ArrivalViewState +import `in`.koreatech.bus.viewstate.CommonTimetableViewState +import `in`.koreatech.koin.core.designsystem.component.tab.KoinSurface +import `in`.koreatech.koin.core.designsystem.theme.KoinTheme +import `in`.koreatech.koin.feature.bus.R + +@Composable +internal fun CommonTimetableView( + timetable: CommonTimetableViewState, + modifier: Modifier = Modifier +) { + KoinSurface( + modifier = modifier + ) { + Column( + modifier = Modifier.padding(horizontal = 24.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth().padding(top = 16.dp) + ) { + Column( + modifier = Modifier.weight(1f).padding(end = 8.dp) + ) { + Text( + text = stringResource(R.string.am), + style = KoinTheme.typography.regular16, + color = KoinTheme.colors.neutral600 + ) + + timetable.arrivals[DaytimeType.AM]?.forEach { + CommonTimetableItem( + arrival = it, + textStyle = KoinTheme.typography.bold18.copy( + color = KoinTheme.colors.warning500 + ), + modifier = Modifier + ) + } + } + + Column( + modifier = Modifier.weight(1f).padding(start = 8.dp) + ) { + Text( + text = stringResource(R.string.pm), + style = KoinTheme.typography.regular16, + color = KoinTheme.colors.neutral600 + ) + + timetable.arrivals[DaytimeType.PM]?.forEach { + CommonTimetableItem( + arrival = it, + textStyle = KoinTheme.typography.bold18.copy( + color = KoinTheme.colors.info700 + ), + modifier = Modifier + ) + } + } + } + + Text( + modifier = Modifier.padding(vertical = 8.dp), + text = stringResource(R.string.updated_at, timetable.updatedAt), + style = KoinTheme.typography.regular14, + color = KoinTheme.colors.neutral500 + ) + } + } +} + +@Preview +@Composable +private fun CommonTimetableViewPreview() { + CommonTimetableView( + modifier = Modifier.fillMaxSize(), + timetable = CommonTimetableViewState( + updatedAt = "2024-09-21", + arrivals = mapOf( + DaytimeType.AM to listOf( + ArrivalViewState( + arrivalTime = "09:00" + ), + ArrivalViewState( + arrivalTime = "09:30" + ), + ArrivalViewState( + arrivalTime = "10:00" + ), + ArrivalViewState( + arrivalTime = "10:30" + ), + ), DaytimeType.PM to listOf( + ArrivalViewState( + arrivalTime = "14:30" + ), + ArrivalViewState( + arrivalTime = "21:00" + ), + ArrivalViewState( + arrivalTime = "21:30" + ), + ArrivalViewState( + arrivalTime = "22:00" + ), + ArrivalViewState( + arrivalTime = "22:30" + ), + ) + ) + ) + ) +} diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/ExpressTimetableScreen.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/ExpressTimetableScreen.kt new file mode 100644 index 000000000..e975e919e --- /dev/null +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/ExpressTimetableScreen.kt @@ -0,0 +1,124 @@ +package `in`.koreatech.bus.screen.timetable.composable + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import `in`.koreatech.bus.screen.timetable.type.CommonDirectionType +import `in`.koreatech.bus.screen.timetable.type.DaytimeType +import `in`.koreatech.bus.viewstate.ArrivalViewState +import `in`.koreatech.bus.viewstate.CommonTimetableViewState +import `in`.koreatech.koin.core.designsystem.component.chip.TextChipGroup +import `in`.koreatech.koin.core.designsystem.theme.KoinTheme +import `in`.koreatech.koin.feature.bus.R + +@Composable +internal fun ExpressTimetableScreen( + timetable: CommonTimetableViewState, + modifier: Modifier = Modifier, + onDirectionChanged: (CommonDirectionType) -> Unit = {} +) { + + var selectedDirectionType by rememberSaveable { mutableStateOf(CommonDirectionType.TO_BYEONGCHEON) } + val context = LocalContext.current + + Column( + modifier = modifier + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp, horizontal = 24.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(R.string.starting_point), + style = KoinTheme.typography.regular16, + color = KoinTheme.colors.neutral600 + ) + + TextChipGroup( + modifier = Modifier.padding(start = 16.dp), + titles = CommonDirectionType.entries.map { stringResource(it.titleRes) }, + onChipSelected = { title -> + selectedDirectionType = + CommonDirectionType.entries.find { context.getString(it.titleRes) == title } ?: CommonDirectionType.TO_BYEONGCHEON + }, + selectedChipIndexes = intArrayOf(selectedDirectionType.ordinal) + ) + } + + CommonTimetableView( + timetable = timetable, + modifier = Modifier.fillMaxSize() + ) + } + + LaunchedEffect(selectedDirectionType) { + onDirectionChanged(selectedDirectionType) + } +} + +@Preview(showBackground = true) +@Composable +private fun ExpressTimetableScreenPreview() { + ExpressTimetableScreen( + modifier = Modifier.fillMaxSize().background(KoinTheme.colors.neutral100), + timetable = CommonTimetableViewState( + updatedAt = "2024-09-21", + arrivals = mapOf( + DaytimeType.AM to listOf( + ArrivalViewState( + arrivalTime = "09:00" + ), + ArrivalViewState( + arrivalTime = "09:30" + ), + ArrivalViewState( + arrivalTime = "10:00" + ), + ArrivalViewState( + arrivalTime = "10:30" + ), + ), DaytimeType.PM to listOf( + ArrivalViewState( + arrivalTime = "14:30" + ), + ArrivalViewState( + arrivalTime = "21:00" + ), + ArrivalViewState( + arrivalTime = "21:30" + ), + ArrivalViewState( + arrivalTime = "22:00" + ), + ArrivalViewState( + arrivalTime = "22:30" + ), + ArrivalViewState( + arrivalTime = "23:00" + ), + ArrivalViewState( + arrivalTime = "23:30" + ) + ), + ) + ) + ) +} \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/ShuttleTimetableScreen.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/ShuttleTimetableScreen.kt index b084e5e70..5367258eb 100644 --- a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/ShuttleTimetableScreen.kt +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/composable/ShuttleTimetableScreen.kt @@ -14,7 +14,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -40,7 +40,7 @@ internal fun ShuttleTimetableScreen( regions: ImmutableList ) { - var selectedRouteTypeIndex by rememberSaveable { mutableIntStateOf(ShuttleBusRouteType.ALL.ordinal) } + var selectedRouteType by rememberSaveable { mutableStateOf(ShuttleBusRouteType.ALL) } val context = LocalContext.current Column( @@ -50,9 +50,9 @@ internal fun ShuttleTimetableScreen( modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp, horizontal = 24.dp), titles = ShuttleBusRouteType.entries.map { stringResource(it.titleRes) }, onChipSelected = { title -> - selectedRouteTypeIndex = ShuttleBusRouteType.entries.find { context.getString(it.titleRes) == title }?.ordinal ?: 0 + selectedRouteType = ShuttleBusRouteType.entries.find { context.getString(it.titleRes) == title } ?: ShuttleBusRouteType.ALL }, - selectedChipIndexes = intArrayOf(selectedRouteTypeIndex) + selectedChipIndexes = intArrayOf(selectedRouteType.ordinal) ) regions.forEach { ShuttleRegionView( diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/BusType.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/BusType.kt index 3f7cdc463..4b0758096 100644 --- a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/BusType.kt +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/BusType.kt @@ -1,11 +1,14 @@ package `in`.koreatech.bus.screen.timetable.type +import android.os.Parcelable import androidx.annotation.StringRes import `in`.koreatech.koin.feature.bus.R +import kotlinx.parcelize.Parcelize +@Parcelize internal enum class BusType( @StringRes val titleRes: Int -) { +) : Parcelable { SHUTTLE(R.string.tab_shuttle), EXPRESS(R.string.tab_express), CITY(R.string.tab_city), diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/CityBusNumberType.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/CityBusNumberType.kt new file mode 100644 index 000000000..5fa54bd36 --- /dev/null +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/CityBusNumberType.kt @@ -0,0 +1,13 @@ +package `in`.koreatech.bus.screen.timetable.type + +import android.os.Parcelable +import androidx.annotation.StringRes +import `in`.koreatech.koin.feature.bus.R +import kotlinx.parcelize.Parcelize + +@Parcelize +enum class CityBusNumberType( + @StringRes val titleRes: Int +) : Parcelable { + N400(R.string.n400), N405(R.string.n405), N495(R.string.n495) +} \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/CommonDirectionType.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/CommonDirectionType.kt new file mode 100644 index 000000000..6997f09c3 --- /dev/null +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/CommonDirectionType.kt @@ -0,0 +1,14 @@ +package `in`.koreatech.bus.screen.timetable.type + +import android.os.Parcelable +import androidx.annotation.StringRes +import `in`.koreatech.koin.feature.bus.R +import kotlinx.parcelize.Parcelize + +@Parcelize +enum class CommonDirectionType( + @StringRes val titleRes: Int +) : Parcelable { + TO_BYEONGCHEON(R.string.to_byeongcheon), + TO_CHEONAN(R.string.to_cheonan), +} \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/DaytimeType.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/DaytimeType.kt new file mode 100644 index 000000000..a39fc6a8f --- /dev/null +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/DaytimeType.kt @@ -0,0 +1,5 @@ +package `in`.koreatech.bus.screen.timetable.type + +enum class DaytimeType { + AM, PM +} diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/ShuttleBusRouteType.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/ShuttleBusRouteType.kt index b84afe6d4..3bd51024b 100644 --- a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/ShuttleBusRouteType.kt +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/type/ShuttleBusRouteType.kt @@ -1,12 +1,15 @@ package `in`.koreatech.bus.screen.timetable.type +import android.os.Parcelable import androidx.annotation.StringRes import `in`.koreatech.koin.feature.bus.R +import kotlinx.parcelize.Parcelize +@Parcelize enum class ShuttleBusRouteType( @StringRes val titleRes: Int, @StringRes val simpleTitleRes: Int -) { +) : Parcelable { ALL(R.string.all_routes, 0), WEEKDAY(R.string.weekday_routes, R.string.weekday_routes_simple), WEEKEND(R.string.weekend_routes, R.string.weekend_routes_simple), diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/viewmodel/BusTimetableViewModel.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/viewmodel/BusTimetableViewModel.kt index ffd834b4a..bc7d890ad 100644 --- a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/viewmodel/BusTimetableViewModel.kt +++ b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/viewmodel/BusTimetableViewModel.kt @@ -1,12 +1,271 @@ package `in`.koreatech.bus.screen.timetable.viewmodel import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import `in`.koreatech.bus.screen.timetable.type.DaytimeType +import `in`.koreatech.bus.screen.timetable.type.ShuttleBusRouteType +import `in`.koreatech.bus.viewstate.ArrivalViewState +import `in`.koreatech.bus.viewstate.CommonTimetableViewState +import `in`.koreatech.bus.viewstate.ShuttleRegionViewState +import `in`.koreatech.bus.viewstate.ShuttleTimetableOverviewViewState +import `in`.koreatech.koin.core.onboarding.OnboardingManager +import `in`.koreatech.koin.core.onboarding.OnboardingType +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class BusTimetableViewModel @Inject constructor( - + private val onboardingManager: OnboardingManager ) : ViewModel() { + /** 임시 데이터 모음 */ + val shuttleRegions = flow { + emit( + listOf( + ShuttleRegionViewState( + name = "서울", + timetableOverviews = listOf( + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.WEEKDAY, + name = "서울-대전", + ), + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.WEEKEND, + name = "서울-대전", + ), + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.CIRCULATION, + name = "서울-대전", + ) + ) + ), + ShuttleRegionViewState( + name = "대전", + timetableOverviews = listOf( + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.WEEKDAY, + name = "대전-서울", + ), + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.WEEKEND, + name = "대전-서울", + description = "토요일, 일요일 운행" + ), + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.CIRCULATION, + name = "대전-서울", + description = "토요일, 천안아산역" + ) + ) + ), + ShuttleRegionViewState( + name = "대구", + timetableOverviews = listOf( + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.WEEKDAY, + name = "대구-서울", + ), + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.WEEKDAY, + name = "대구-서울", + ), + ShuttleTimetableOverviewViewState( + routeType = ShuttleBusRouteType.WEEKEND, + name = "대구-서울", + description = "금요일 하교 추가" + ) + ) + ) + ) + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = emptyList() + ) + val expressTimetable = flow { + emit( + CommonTimetableViewState( + updatedAt = "2024-09-21", + arrivals = mapOf( + DaytimeType.AM to listOf( + ArrivalViewState( + arrivalTime = "09:00" + ), + ArrivalViewState( + arrivalTime = "09:30" + ), + ArrivalViewState( + arrivalTime = "10:00" + ), + ArrivalViewState( + arrivalTime = "10:30" + ), + ), DaytimeType.PM to listOf( + ArrivalViewState( + arrivalTime = "14:30" + ), + ArrivalViewState( + arrivalTime = "21:00" + ), + ArrivalViewState( + arrivalTime = "21:30" + ), + ArrivalViewState( + arrivalTime = "22:00" + ), + ArrivalViewState( + arrivalTime = "22:30" + ), + ArrivalViewState( + arrivalTime = "23:00" + ), + ArrivalViewState( + arrivalTime = "23:30" + ) + ), + ) + ) + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = CommonTimetableViewState( + updatedAt = "2024-09-21", + arrivals = mapOf( + DaytimeType.AM to listOf( + ArrivalViewState( + arrivalTime = "09:00" + ), + ArrivalViewState( + arrivalTime = "09:30" + ), + ArrivalViewState( + arrivalTime = "10:00" + ), + ArrivalViewState( + arrivalTime = "10:30" + ), + ), DaytimeType.PM to listOf( + ArrivalViewState( + arrivalTime = "14:30" + ), + ArrivalViewState( + arrivalTime = "21:00" + ), + ArrivalViewState( + arrivalTime = "21:30" + ), + ArrivalViewState( + arrivalTime = "22:00" + ), + ArrivalViewState( + arrivalTime = "22:30" + ), + ArrivalViewState( + arrivalTime = "23:00" + ), + ArrivalViewState( + arrivalTime = "23:30" + ) + ), + ) + ) + ) + val cityTimetable = flow { + emit( + CommonTimetableViewState( + updatedAt = "2024-09-21", + arrivals = mapOf( + DaytimeType.AM to listOf( + ArrivalViewState( + arrivalTime = "09:00" + ), + ArrivalViewState( + arrivalTime = "09:30" + ), + ArrivalViewState( + arrivalTime = "10:00" + ), + ArrivalViewState( + arrivalTime = "10:30" + ), + ), DaytimeType.PM to listOf( + ArrivalViewState( + arrivalTime = "14:30" + ) + ) + ) + ) + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = CommonTimetableViewState( + updatedAt = "2024-09-21", + arrivals = mapOf( + DaytimeType.AM to listOf( + ArrivalViewState( + arrivalTime = "09:00" + ), + ArrivalViewState( + arrivalTime = "09:30" + ), + ArrivalViewState( + arrivalTime = "10:00" + ), + ArrivalViewState( + arrivalTime = "10:30" + ), + ), DaytimeType.PM to listOf( + ArrivalViewState( + arrivalTime = "14:30" + ), + ArrivalViewState( + arrivalTime = "21:00" + ), + ArrivalViewState( + arrivalTime = "21:30" + ), + ArrivalViewState( + arrivalTime = "22:00" + ), + ArrivalViewState( + arrivalTime = "22:30" + ), + ArrivalViewState( + arrivalTime = "23:00" + ), + ArrivalViewState( + arrivalTime = "23:30" + ) + ), + ) + ) + ) + val notice = flow { + emit("[긴급] 9.27(금) 대학등교방향 천안셔틀버스 터미널 미정차 알림(천안역에서 승차바람)") + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = "[긴급] 9.27(금) 대학등교방향 천안셔틀버스 터미널 미정차 알림(천안역에서 승차바람)" + ) + + val shouldShowNotice = onboardingManager.getShouldOnboardFlow( + OnboardingType.SHOW_BUS_HEAD_ARTICLE + ).stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = false + ) + + fun closeNotice() { + viewModelScope.launch { + onboardingManager.updateShouldOnboard(OnboardingType.SHOW_BUS_HEAD_ARTICLE, false) + } + } } \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/viewmodel/BusViewModel.kt b/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/viewmodel/BusViewModel.kt deleted file mode 100644 index aa0234d35..000000000 --- a/feature/bus/src/main/java/in/koreatech/bus/screen/timetable/viewmodel/BusViewModel.kt +++ /dev/null @@ -1,11 +0,0 @@ -package `in`.koreatech.bus.screen.timetable.viewmodel - -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject - -@HiltViewModel -class BusViewModel @Inject constructor( - -) : ViewModel() { -} \ No newline at end of file diff --git a/feature/bus/src/main/java/in/koreatech/bus/viewstate/CommonTimetableViewState.kt b/feature/bus/src/main/java/in/koreatech/bus/viewstate/CommonTimetableViewState.kt new file mode 100644 index 000000000..8051bfffc --- /dev/null +++ b/feature/bus/src/main/java/in/koreatech/bus/viewstate/CommonTimetableViewState.kt @@ -0,0 +1,16 @@ +package `in`.koreatech.bus.viewstate + +import androidx.compose.runtime.Immutable +import `in`.koreatech.bus.screen.timetable.type.DaytimeType + +@Immutable +data class CommonTimetableViewState( + val arrivals: Map>, + val updatedAt: String +) + +data class ArrivalViewState( + val arrivalTime: String, +) + +// TODO : 임시 데이터, API 아직 없음 \ No newline at end of file diff --git a/feature/bus/src/main/res/layout/activity_bus2.xml b/feature/bus/src/main/res/layout/activity_bus2.xml deleted file mode 100644 index 684c98256..000000000 --- a/feature/bus/src/main/res/layout/activity_bus2.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/feature/bus/src/main/res/values/strings.xml b/feature/bus/src/main/res/values/strings.xml index caaed9e72..3e3d52010 100644 --- a/feature/bus/src/main/res/values/strings.xml +++ b/feature/bus/src/main/res/values/strings.xml @@ -1,7 +1,9 @@ 버스 시간표 - 교내 셔틀 시간표 + 셔틀버스 시간표 + 대성버스 시간표 + 시내버스 시간표 정보가 정확하지 않나요? 셔틀 대성 @@ -13,6 +15,17 @@ 주중 주말 순환 + 병천방면 + 천안방면 + 오전 + 오후 + 업데이트 날짜 : %s + 400번 + 405번 + 495번 + 기점 + 노선 + 운행