Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import io.soma.cryptobook.core.domain.model.CoinPriceVO
import io.soma.cryptobook.core.presentation.Event
import io.soma.cryptobook.core.presentation.SideEffect
import io.soma.cryptobook.core.presentation.UiState
import io.soma.cryptobook.home.presentation.component.sortheader.SortDirection
import java.math.BigDecimal

data class HomeUiState(
val coins: List<CoinItem> = emptyList(),
val isLoading: Boolean = false,
val errorMsg: String? = null,
val sortField: SortField = SortField.Price,
val sortDirection: SortDirection = SortDirection.Desc,
Comment on lines 10 to +15
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HomeUiState now exposes sortDirection using SortDirection from the component.sortheader package, which couples the screen contract to a specific UI component implementation. In other screens (e.g. coin-detail/presentation/src/main/java/io/soma/cryptobook/coindetail/presentation/CoinDetailContract.kt), UI state types stay independent of leaf UI components, so it would be more consistent and maintainable to define SortDirection alongside HomeUiState (or in a shared presentation package) and let SortHeader/SortHeaderItem depend on that instead.

Copilot uses AI. Check for mistakes.
) : UiState

data class CoinItem(
Expand All @@ -19,10 +22,17 @@ data class CoinItem(
val priceChangePercentage24h: Double,
)

enum class SortField {
Symbol,
Price,
Change,
}

sealed interface HomeEvent : Event {
data object OnRefresh : HomeEvent
data object OnBackClicked : HomeEvent
data class OnCoinClicked(val symbol: String) : HomeEvent
data class OnSortClicked(val field: SortField) : HomeEvent
}

sealed interface HomeSideEffect : SideEffect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,21 @@ internal fun HomeScreen(state: HomeUiState, onEvent: (HomeEvent) -> Unit, modifi
}
}

// Sort Header (TODO: 정렬 기능 구현)
// Sort Header
val symbolSort =
if (state.sortField == SortField.Symbol) state.sortDirection else SortDirection.None
val priceSort =
if (state.sortField == SortField.Price) state.sortDirection else SortDirection.None
val changeSort =
if (state.sortField == SortField.Change) state.sortDirection else SortDirection.None

SortHeader(
symbolSort = SortDirection.None,
priceSort = SortDirection.Desc,
changeSort = SortDirection.None,
onSymbolClick = { /* TODO: 정렬 기능 구현 */ },
onPriceClick = { /* TODO: 정렬 기능 구현 */ },
onChangeClick = { /* TODO: 정렬 기능 구현 */ },
symbolSort = symbolSort,
priceSort = priceSort,
changeSort = changeSort,
onSymbolClick = { onEvent(HomeEvent.OnSortClicked(SortField.Symbol)) },
onPriceClick = { onEvent(HomeEvent.OnSortClicked(SortField.Price)) },
onChangeClick = { onEvent(HomeEvent.OnSortClicked(SortField.Change)) },
Comment on lines +79 to +81
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명시적으로 넣는거보다 state.sortField를 인자로 넣어도 되겠네요

)

// Coin List
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.soma.cryptobook.core.domain.navigation.AppPage
import io.soma.cryptobook.core.domain.navigation.NavigationHelper
import io.soma.cryptobook.core.presentation.MviViewModel
import io.soma.cryptobook.home.domain.usecase.ObserveCoinListUseCase
import io.soma.cryptobook.home.presentation.component.sortheader.SortDirection
import javax.inject.Inject

@HiltViewModel
Expand All @@ -31,6 +32,7 @@ class HomeViewModel @Inject constructor(
is HomeEvent.OnCoinClicked -> navigationHelper.navigate(
AppPage.CoinDetail(event.symbol),
)
is HomeEvent.OnSortClicked -> updateSort(event.field)
}
}

Expand All @@ -39,11 +41,16 @@ class HomeViewModel @Inject constructor(
observeCoinListUseCase().collect { result ->
when (result) {
is ObserveCoinListUseCase.Result.Success -> {
val sortedCoins = sortCoins(
coins = result.coinList.map { it.toCoinItem() },
sortField = currentState.sortField,
sortDirection = currentState.sortDirection,
)
reduce {
copy(
isLoading = false,
errorMsg = null,
coins = result.coinList.map {
coins = sortedCoins.coinList.map {
it.toCoinItem(coinImageResolver.getImageUrl(it.symbol))
},
)
Expand All @@ -63,4 +70,49 @@ class HomeViewModel @Inject constructor(
}
}
}

private fun updateSort(field: SortField) {
val newDirection = if (field == currentState.sortField) {
toggleDirection(currentState.sortDirection)
} else {
defaultSortDirection(field)
}

reduce {
copy(
sortField = field,
sortDirection = newDirection,
coins = sortCoins(coins, field, newDirection),
)
}
}

private fun sortCoins(
coins: List<CoinItem>,
sortField: SortField,
sortDirection: SortDirection,
): List<CoinItem> {
val comparator = when (sortField) {
SortField.Symbol -> compareBy<CoinItem> { it.symbol }
SortField.Price -> compareBy<CoinItem> { it.price }
SortField.Change -> compareBy<CoinItem> { it.priceChangePercentage24h }
}

return if (sortDirection == SortDirection.Desc) {
coins.sortedWith(comparator).asReversed()
}else{
coins.sortedWith(comparator)
Comment on lines +101 to +104
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if/else branch uses a }else{ style that differs from the rest of the Kotlin codebase, which consistently uses spaces (} else {, e.g. core/network/src/main/java/io/soma/cryptobook/core/network/base/BaseDataSource.kt:38). For consistency with established formatting conventions (and to keep tools like Spotless/ktlint happy), please add spaces around else here.

Copilot uses AI. Check for mistakes.
}
}

private fun toggleDirection(current: SortDirection): SortDirection {
return if (current == SortDirection.Asc) SortDirection.Desc else SortDirection.Asc
}

private fun defaultSortDirection(field: SortField): SortDirection {
return when (field) {
SortField.Symbol -> SortDirection.Asc
SortField.Price, SortField.Change -> SortDirection.Desc
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fun SortHeaderItem(
text = label,
fontFamily = fontFamily,
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
fontSize = 12.sp,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lineHeight = 20.sp 이기 때문에 fontSize를 바꾸는 것은 의미가 없습니다. 바꾼 이유가 있을까요?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

24h change % 부분이 가로로 길이가 초과되서요

lineHeight = 20.sp,
color = textColor,
)
Expand Down