diff --git a/app/src/main/java/com/into/websoso/data/remote/api/NovelApi.kt b/app/src/main/java/com/into/websoso/data/remote/api/NovelApi.kt index 7603334fb..2262d9a6a 100644 --- a/app/src/main/java/com/into/websoso/data/remote/api/NovelApi.kt +++ b/app/src/main/java/com/into/websoso/data/remote/api/NovelApi.kt @@ -54,7 +54,8 @@ interface NovelApi { suspend fun getFilteredNovelResult( @Query("genres") genres: List?, @Query("isCompleted") isCompleted: Boolean?, - @Query("novelRating") novelRating: Float?, + @Query("novelRatingStart") novelRatingStart: Float, + @Query("novelRatingEnd") novelRatingEnd: Float, @Query("keywordIds") keywordIds: List?, @Query("page") page: Int, @Query("size") size: Int, diff --git a/app/src/main/java/com/into/websoso/data/repository/NovelRepository.kt b/app/src/main/java/com/into/websoso/data/repository/NovelRepository.kt index 456c887ba..eac76144a 100644 --- a/app/src/main/java/com/into/websoso/data/repository/NovelRepository.kt +++ b/app/src/main/java/com/into/websoso/data/repository/NovelRepository.kt @@ -77,7 +77,8 @@ class NovelRepository suspend fun fetchFilteredNovelResult( genres: List?, isCompleted: Boolean?, - novelRating: Float?, + novelRatingStart: Float, + novelRatingEnd: Float, keywordIds: List?, page: Int, size: Int, @@ -85,7 +86,8 @@ class NovelRepository val result = novelApi.getFilteredNovelResult( genres = genres, isCompleted = isCompleted, - novelRating = novelRating, + novelRatingStart = novelRatingStart, + novelRatingEnd = novelRatingEnd, keywordIds = keywordIds, page = page, size = size, diff --git a/app/src/main/java/com/into/websoso/domain/usecase/GetDetailExploreResultUseCase.kt b/app/src/main/java/com/into/websoso/domain/usecase/GetDetailExploreResultUseCase.kt index 9bc8c158b..c3ddd4afb 100644 --- a/app/src/main/java/com/into/websoso/domain/usecase/GetDetailExploreResultUseCase.kt +++ b/app/src/main/java/com/into/websoso/domain/usecase/GetDetailExploreResultUseCase.kt @@ -13,18 +13,20 @@ class GetDetailExploreResultUseCase ) { private var previousGenres: List? = null private var previousIsCompleted: Boolean? = null - private var previousNovelRating: Float? = null + private var previousNovelRatingStart: Float = RATING_MIN_DEFAULT + private var previousNovelRatingEnd: Float = RATING_MAX_DEFAULT private var previousKeywordIds: List? = null private var previousPage: Int = INITIAL_PAGE suspend operator fun invoke( genres: List?, isCompleted: Boolean?, - novelRating: Float?, + novelRatingStart: Float, + novelRatingEnd: Float, keywordIds: List?, isSearchButtonClick: Boolean, ): ExploreResult { - if (isSearchButtonClick && isCacheValid(genres, isCompleted, novelRating, keywordIds)) { + if (isSearchButtonClick && isCacheValid(genres, isCompleted, novelRatingStart, novelRatingEnd, keywordIds)) { return ExploreResultEntity( resultCount = novelRepository.cachedNormalExploreResult.size.toLong(), isLoadable = novelRepository.cachedDetailExploreIsLoadable, @@ -45,7 +47,8 @@ class GetDetailExploreResultUseCase .fetchFilteredNovelResult( genres = genres, isCompleted = isCompleted, - novelRating = novelRating, + novelRatingStart = novelRatingStart, + novelRatingEnd = novelRatingEnd, keywordIds = keywordIds, page = previousPage, size = REQUEST_SIZE, @@ -53,7 +56,8 @@ class GetDetailExploreResultUseCase .also { previousGenres = genres previousIsCompleted = isCompleted - previousNovelRating = novelRating + previousNovelRatingStart = novelRatingStart + previousNovelRatingEnd = novelRatingEnd previousKeywordIds = keywordIds } } @@ -61,17 +65,21 @@ class GetDetailExploreResultUseCase private fun isCacheValid( genres: List?, isCompleted: Boolean?, - novelRating: Float?, + novelRatingStart: Float, + novelRatingEnd: Float, keywordIds: List?, ): Boolean = genres?.equals(previousGenres) == true && isCompleted == previousIsCompleted && - novelRating == previousNovelRating && + novelRatingStart == previousNovelRatingStart && + novelRatingEnd == previousNovelRatingEnd && keywordIds?.equals(previousKeywordIds) == true companion object { private const val INITIAL_PAGE = 0 private const val ADDITIONAL_PAGE_SIZE = 1 private const val REQUEST_SIZE = 30 + private const val RATING_MIN_DEFAULT = 0.0f + private const val RATING_MAX_DEFAULT = 5.0f } } diff --git a/app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreActivity.kt b/app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreActivity.kt index 1a55a3e19..19bf58cd8 100644 --- a/app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreActivity.kt +++ b/app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreActivity.kt @@ -39,7 +39,10 @@ class DetailExploreActivity : AppCompatActivity() { private fun navigateToSearchResult() { val selectedGenres = detailExploreViewModel.selectedGenres.value ?: emptyList() val isCompleted = detailExploreViewModel.selectedStatus.value?.isCompleted - val novelRating = detailExploreViewModel.selectedRating.value + val novelRatingStart = detailExploreViewModel.selectedRatingMin.value + ?: DetailExploreViewModel.RATING_MIN + val novelRatingEnd = detailExploreViewModel.selectedRatingMax.value + ?: DetailExploreViewModel.RATING_MAX val keywordIds = detailExploreViewModel.uiState.value ?.categories @@ -54,7 +57,8 @@ class DetailExploreActivity : AppCompatActivity() { detailExploreFilteredModel = DetailExploreFilteredModel( genres = selectedGenres, isCompleted = isCompleted, - novelRating = novelRating, + novelRatingStart = novelRatingStart, + novelRatingEnd = novelRatingEnd, keywordIds = keywordIds, ), ), diff --git a/app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt b/app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt index cb2945b2e..698893cdb 100644 --- a/app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt +++ b/app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt @@ -29,15 +29,10 @@ class DetailExploreViewModel private val _selectedStatus: MutableLiveData = MutableLiveData() val selectedStatus: LiveData get() = _selectedStatus - private val _selectedRatingMin: MutableLiveData = MutableLiveData(RATING_MIN) - val selectedRatingMin: LiveData get() = _selectedRatingMin - - private val _selectedRatingMax: MutableLiveData = MutableLiveData(RATING_MAX) - val selectedRatingMax: LiveData get() = _selectedRatingMax - - val selectedRating: LiveData = _selectedRatingMin.map { min -> - if (min > RATING_MIN) min else null - } + private val selectedRatingRange: MutableLiveData = + MutableLiveData(RatingRange(RATING_MIN, RATING_MAX)) + val selectedRatingMin: LiveData = selectedRatingRange.map { it.min } + val selectedRatingMax: LiveData = selectedRatingRange.map { it.max } private val _isInfoChipSelected: MediatorLiveData = MediatorLiveData(false) val isInfoChipSelected: LiveData get() = _isInfoChipSelected @@ -56,10 +51,7 @@ class DetailExploreViewModel _isInfoChipSelected.addSource(_selectedStatus) { _isInfoChipSelected.value = isInfoChipSelectedEnabled() } - _isInfoChipSelected.addSource(_selectedRatingMin) { - _isInfoChipSelected.value = isInfoChipSelectedEnabled() - } - _isInfoChipSelected.addSource(_selectedRatingMax) { + _isInfoChipSelected.addSource(selectedRatingRange) { _isInfoChipSelected.value = isInfoChipSelectedEnabled() } @@ -69,9 +61,8 @@ class DetailExploreViewModel private fun isInfoChipSelectedEnabled(): Boolean { val isGenreChipSelected: Boolean = _selectedGenres.value?.isNotEmpty() == true val isStatusChipSelected: Boolean = _selectedStatus.value != null - val isRatingRangeNarrowed: Boolean = - (_selectedRatingMin.value ?: RATING_MIN) > RATING_MIN || - (_selectedRatingMax.value ?: RATING_MAX) < RATING_MAX + val range = selectedRatingRange.value ?: RatingRange(RATING_MIN, RATING_MAX) + val isRatingRangeNarrowed: Boolean = range.min > RATING_MIN || range.max < RATING_MAX return isGenreChipSelected || isStatusChipSelected || isRatingRangeNarrowed } @@ -79,8 +70,7 @@ class DetailExploreViewModel fun updateSelectedInfoValueClear() { _selectedGenres.value = mutableListOf() _selectedStatus.value = null - _selectedRatingMin.value = RATING_MIN - _selectedRatingMax.value = RATING_MAX + selectedRatingRange.value = RatingRange(RATING_MIN, RATING_MAX) } fun updateSelectedGenres(genre: Genre) { @@ -107,8 +97,7 @@ class DetailExploreViewModel min: Float, max: Float, ) { - _selectedRatingMin.value = min - _selectedRatingMax.value = max + selectedRatingRange.value = RatingRange(min, max) } fun updateKeyword(searchWord: String?) { @@ -204,6 +193,11 @@ class DetailExploreViewModel } } + private data class RatingRange( + val min: Float, + val max: Float, + ) + companion object { const val RATING_MIN = 0.0f const val RATING_MAX = 5.0f diff --git a/app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreInfoTab.kt b/app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreInfoTab.kt index a39ed8ad4..6b1fe8498 100644 --- a/app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreInfoTab.kt +++ b/app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreInfoTab.kt @@ -9,12 +9,12 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio 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.layout.width -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -78,7 +78,6 @@ fun DetailExploreInfoTab( max = ratingMax, onRangeChange = viewModel::updateSelectedRatingRange, ) - Spacer(modifier = Modifier.height(8.dp)) } } @@ -117,6 +116,10 @@ private fun GenreSection( label = genre.titleKr, isSelected = selectedGenres.contains(genre), onClick = { onGenreClick(genre) }, + modifier = Modifier.size( + width = genre.figmaWidthDp.dp, + height = 37.dp, + ), ) } } @@ -166,17 +169,19 @@ private fun StatusChipCell( label = label, isSelected = selectedStatus == status, onClick = { onClick(status) }, - modifier = modifier.wrapContentHeight(), + modifier = modifier.aspectRatio(STATUS_CHIP_ASPECT_RATIO), ) } +private const val STATUS_CHIP_ASPECT_RATIO = 155f / 43f + @Composable private fun RatingSection( min: Float, max: Float, onRangeChange: (Float, Float) -> Unit, ) { - Column { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { Row( modifier = Modifier .fillMaxWidth() @@ -196,7 +201,6 @@ private fun RatingSection( color = Primary100, ) } - Spacer(modifier = Modifier.height(6.dp)) Row( modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/com/into/websoso/ui/detailExplore/info/model/Genre.kt b/app/src/main/java/com/into/websoso/ui/detailExplore/info/model/Genre.kt index 6296bcfa7..d32d7106c 100644 --- a/app/src/main/java/com/into/websoso/ui/detailExplore/info/model/Genre.kt +++ b/app/src/main/java/com/into/websoso/ui/detailExplore/info/model/Genre.kt @@ -3,16 +3,17 @@ package com.into.websoso.ui.detailExplore.info.model enum class Genre( val titleKr: String, val titleEn: String, + val figmaWidthDp: Int, ) { - ROMANCE("로맨스", "romance"), - ROMANCE_FANTASY("로판", "romanceFantasy"), - FANTASY("판타지", "fantasy"), - MODERN_FANTASY("현판", "modernFantasy"), - WUXIA("무협", "wuxia"), - MYSTERY("미스터리", "mystery"), - DRAMA("드라마", "drama"), - LIGHT_NOVEL("라노벨", "lightNovel"), - BOYS_LOVE("BL", "bl"), + ROMANCE("로맨스", "romance", 64), + ROMANCE_FANTASY("로판", "romanceFantasy", 52), + FANTASY("판타지", "fantasy", 64), + MODERN_FANTASY("현판", "modernFantasy", 52), + WUXIA("무협", "wuxia", 52), + MYSTERY("미스터리", "mystery", 77), + DRAMA("드라마", "drama", 64), + LIGHT_NOVEL("라노벨", "lightNovel", 64), + BOYS_LOVE("BL", "bl", 43), ; companion object { diff --git a/app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultViewModel.kt b/app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultViewModel.kt index f8ae543a9..08fc35da6 100644 --- a/app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultViewModel.kt +++ b/app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultViewModel.kt @@ -26,7 +26,8 @@ class DetailExploreResultViewModel private val filterGenres: MutableLiveData?> = MutableLiveData(emptyList()) private val filterIsNovelCompleted: MutableLiveData = MutableLiveData() - private val filterRating: MutableLiveData = MutableLiveData() + private val filterRatingStart: MutableLiveData = MutableLiveData(DetailExploreFilteredModel.RATING_MIN_DEFAULT) + private val filterRatingEnd: MutableLiveData = MutableLiveData(DetailExploreFilteredModel.RATING_MAX_DEFAULT) private val filterKeywordIds: MutableLiveData?> = MutableLiveData(emptyList()) private val _appliedFiltersMessage: MediatorLiveData = MediatorLiveData() @@ -39,7 +40,8 @@ class DetailExploreResultViewModel _appliedFiltersMessage.apply { addSource(filterGenres) { updateMessage() } addSource(filterIsNovelCompleted) { updateMessage() } - addSource(filterRating) { updateMessage() } + addSource(filterRatingStart) { updateMessage() } + addSource(filterRatingEnd) { updateMessage() } addSource(filterKeywordIds) { updateMessage() } } } @@ -49,7 +51,7 @@ class DetailExploreResultViewModel if (filterGenres.value?.isNotEmpty() == true) appliedFilters.add(GENRES_LABEL) if (filterIsNovelCompleted.value != null) appliedFilters.add(NOVEL_COMPLETED_LABEL) - if (filterRating.value != null) appliedFilters.add(RATING_LABEL) + appliedFilters.add(RATING_LABEL) if (filterKeywordIds.value?.isNotEmpty() == true) appliedFilters.add(KEYWORDS_LABEL) _appliedFiltersMessage.value = appliedFilters.joinToString(FILTER_SEPARATOR) @@ -58,7 +60,8 @@ class DetailExploreResultViewModel fun updatePreviousSearchFilteredValue(detailExploreFilteredModel: DetailExploreFilteredModel) { filterGenres.value = detailExploreFilteredModel.genres filterIsNovelCompleted.value = detailExploreFilteredModel.isCompleted - filterRating.value = detailExploreFilteredModel.novelRating + filterRatingStart.value = detailExploreFilteredModel.novelRatingStart + filterRatingEnd.value = detailExploreFilteredModel.novelRatingEnd filterKeywordIds.value = detailExploreFilteredModel.keywordIds updateSearchResult(true) @@ -72,7 +75,8 @@ class DetailExploreResultViewModel getDetailExploreResultUseCase( genres = filterGenres.value?.map { it.titleEn }, isCompleted = filterIsNovelCompleted.value, - novelRating = filterRating.value, + novelRatingStart = filterRatingStart.value ?: DetailExploreFilteredModel.RATING_MIN_DEFAULT, + novelRatingEnd = filterRatingEnd.value ?: DetailExploreFilteredModel.RATING_MAX_DEFAULT, keywordIds = filterKeywordIds.value, isSearchButtonClick = isSearchButtonClick, ) diff --git a/app/src/main/java/com/into/websoso/ui/detailExploreResult/model/DetailExploreFilteredModel.kt b/app/src/main/java/com/into/websoso/ui/detailExploreResult/model/DetailExploreFilteredModel.kt index b2ab8f498..f7748b3f5 100644 --- a/app/src/main/java/com/into/websoso/ui/detailExploreResult/model/DetailExploreFilteredModel.kt +++ b/app/src/main/java/com/into/websoso/ui/detailExploreResult/model/DetailExploreFilteredModel.kt @@ -8,6 +8,12 @@ import kotlinx.parcelize.Parcelize data class DetailExploreFilteredModel( val genres: List? = null, val isCompleted: Boolean? = null, - val novelRating: Float? = null, + val novelRatingStart: Float = RATING_MIN_DEFAULT, + val novelRatingEnd: Float = RATING_MAX_DEFAULT, val keywordIds: List? = null, -) : Parcelable +) : Parcelable { + companion object { + const val RATING_MIN_DEFAULT = 0.0f + const val RATING_MAX_DEFAULT = 5.0f + } +} diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 53ee70e1f..8ffff268d 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -100,23 +100,10 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cl_home_normal_search"> - - + + 작품 제목, 작가를 검색하세요 - 뭐 읽을지 고민될 땐? + 뭐 읽을지 고민될 때 장르, 연재상태, 별점, 키워드로 작품 찾기 소소 다른 독자들이 최근에 찾아본 웹소설이에요 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 26e57e574..d952d5fab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] # App Versioning -versionCode = "10050" -versionName = "1.6.3" +versionCode = "10051" +versionName = "1.7.0" # Gradle Plugin & Kotlin android-gradle-plugin = "8.13.2"