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

[Feature] 버스 조회 뷰 #462

Merged
merged 14 commits into from
Nov 8, 2024
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package `in`.koreatech.koin.core.designsystem.component
package `in`.koreatech.koin.core.designsystem.component.dialog

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package `in`.koreatech.koin.domain.error.busv2

sealed class SearchBusError: Throwable() {
class EmptyDeparture: SearchBusError()
class EmptyArrival: SearchBusError()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package `in`.koreatech.koin.domain.usecase.busv2

import `in`.koreatech.koin.domain.error.busv2.SearchBusError
import javax.inject.Inject

class SearchBusV2UseCase @Inject constructor(
// private val busRepository
) {

suspend operator fun invoke(departure: String, arrival: String): Result<Unit> {
return when {
departure.isEmpty() -> Result.failure(SearchBusError.EmptyDeparture())
arrival.isEmpty() -> Result.failure(SearchBusError.EmptyArrival())
else -> {
// TODO repository Search bus
// busRepository.searchBus(departure, arrival)
Result.success(Unit)
}
}
}
}
2 changes: 2 additions & 0 deletions feature/bus/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ dependencies {

implementation("androidx.navigation:navigation-compose:2.8.3")
implementation(libs.kotlinx.serialization.json)

implementation(libs.androidx.constraintlayout.compose)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ 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.search.composable.BusSearchScreen
import `in`.koreatech.bus.screen.timetable.composable.BusTimetableScreen

@Composable
fun BusNavigation(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
viewModel: BusViewModel = hiltViewModel()
) {

NavHost(
Expand All @@ -30,7 +30,20 @@ fun BusNavigation(
) {

composable<Routes.BusTimetable> {
BusTimetableScreen(
modifier = Modifier.fillMaxSize(),
onNavigationIconClick = { navController.popBackStack() }
)
}

composable<Routes.BusSearch> {
BusSearchScreen(
modifier = Modifier.fillMaxSize(),
onNavigationIconClick = { navController.popBackStack() },
onSearchSuccess = {
// navController.navigate(Routes.SearchedTimetable(it))
}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ import kotlinx.serialization.Serializable
internal object Routes {

@Serializable data object BusTimetable
@Serializable data object BusSearch
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package `in`.koreatech.bus.screen.search.composable

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
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.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import `in`.koreatech.koin.core.designsystem.component.button.FilledButton
import `in`.koreatech.koin.core.designsystem.component.tab.KoinSurface
import `in`.koreatech.koin.core.designsystem.noRippleClickable
import `in`.koreatech.koin.core.designsystem.theme.KoinTheme
import `in`.koreatech.koin.feature.bus.R

@Composable
internal fun BusSearchContentView(
departure: String,
arrival: String,
modifier: Modifier = Modifier,
searchButtonEnabled: Boolean = false,
onSwapIconClicked: () -> Unit = {},
onSearchClicked: () -> Unit = {},
onDepartureFieldClicked: () -> Unit = {},
onArrivalFieldClicked: () -> Unit = {}
) {

KoinSurface {
Column(modifier = modifier) {
Text(
modifier = Modifier,
text = stringResource(R.string.introduce_bus_search),
style = KoinTheme.typography.medium16,
color = KoinTheme.colors.neutral800
)

Text(
modifier = Modifier.padding(top = 2.dp),
text = stringResource(R.string.caution_possibly_inaccurate),
style = KoinTheme.typography.regular12,
color = KoinTheme.colors.neutral600
)

ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
.padding(top = 46.dp)
.height(IntrinsicSize.Min)
) {
val (departureText, arrivalText, departureField, arrivalField, iconSwap) = createRefs()

Text(
modifier = Modifier.constrainAs(departureText) {
start.linkTo(departureField.start)
top.linkTo(parent.top)
end.linkTo(departureField.end)
},
text = stringResource(R.string.departure),
style = KoinTheme.typography.medium16,
color = KoinTheme.colors.primary500
)
Text(
modifier = Modifier.constrainAs(arrivalText) {
top.linkTo(parent.top)
start.linkTo(arrivalField.start)
end.linkTo(arrivalField.end)
},
text = stringResource(R.string.arrival),
style = KoinTheme.typography.medium16,
color = KoinTheme.colors.primary500
)

BusSearchInput(
place = departure,
placeholder = stringResource(R.string.select_departure),
modifier = Modifier
.padding(top = 10.dp)
.noRippleClickable {
onDepartureFieldClicked()
}
.constrainAs(departureField) {
top.linkTo(iconSwap.top)
bottom.linkTo(iconSwap.bottom)
start.linkTo(parent.start)
end.linkTo(iconSwap.start)

width = Dimension.fillToConstraints
height = Dimension.preferredWrapContent
}
)

IconButton(
onClick = onSwapIconClicked,
modifier = Modifier
.padding(top = 10.dp)
.padding(horizontal = 16.dp, vertical = 12.dp)
.size(32.dp)
.constrainAs(iconSwap) {
top.linkTo(departureText.bottom)
start.linkTo(departureField.end)
end.linkTo(arrivalField.start)
}
) {
Icon(
painter = painterResource(id = R.drawable.ic_swap), // TODO : 아이콘 깨짐
contentDescription = stringResource(R.string.swap_content_description),
kongwoojin marked this conversation as resolved.
Show resolved Hide resolved
)
}

BusSearchInput(
place = arrival,
placeholder = stringResource(R.string.select_arrival),
modifier = Modifier
.padding(top = 10.dp)
.noRippleClickable {
onArrivalFieldClicked()
}
.constrainAs(arrivalField) {
top.linkTo(iconSwap.top)
bottom.linkTo(iconSwap.bottom)
start.linkTo(iconSwap.end)
end.linkTo(parent.end)

width = Dimension.fillToConstraints
height = Dimension.preferredWrapContent
}
)
}

Spacer(modifier = Modifier.weight(1f))
FilledButton(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 30.dp),
enabled = searchButtonEnabled,
text = stringResource(R.string.search),
contentPadding = PaddingValues(vertical = 12.dp),
onClick = onSearchClicked
)
}
}
}

@Composable
private fun BusSearchInput(
place: String,
placeholder: String,
modifier: Modifier = Modifier,
) {

val isPlaceDetermined = place.isNotEmpty()

Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(4.dp))
.background(if (isPlaceDetermined.not()) KoinTheme.colors.neutral100 else Color.Transparent)
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
if (isPlaceDetermined.not())
Text(
text = placeholder,
maxLines = 1,
style = KoinTheme.typography.regular14,
color = KoinTheme.colors.neutral400 // TODO neutral450 ?
)
else {
Text(
text = place,
maxLines = 1,
style = KoinTheme.typography.bold18,
color = KoinTheme.colors.neutral800
)
}
}
}
}
Loading