Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package com.into.websoso.data.mapper

import com.into.websoso.data.model.CategoriesEntity
import com.into.websoso.data.remote.response.KeywordsResponseDto
import com.into.websoso.data.remote.response.PopularKeywordsResponseDto

fun KeywordsResponseDto.toData(): CategoriesEntity =
CategoriesEntity(
categories = categories.map { it.toData() },
)

fun PopularKeywordsResponseDto.toData(): List<CategoriesEntity.CategoryEntity.KeywordEntity> = keywords.map { it.toData() }

fun KeywordsResponseDto.CategoryResponseDto.toData(): CategoriesEntity.CategoryEntity =
CategoriesEntity.CategoryEntity(
categoryName = categoryName,
Expand All @@ -20,3 +23,9 @@ fun KeywordsResponseDto.CategoryResponseDto.KeywordResponseDto.toData(): Categor
keywordId = keywordId,
keywordName = keywordName,
)

fun PopularKeywordsResponseDto.KeywordResponseDto.toData(): CategoriesEntity.CategoryEntity.KeywordEntity =
CategoriesEntity.CategoryEntity.KeywordEntity(
keywordId = keywordId,
keywordName = keywordName,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.into.websoso.data.remote.api

import com.into.websoso.data.remote.response.KeywordsResponseDto
import com.into.websoso.data.remote.response.PopularKeywordsResponseDto
import retrofit2.http.GET
import retrofit2.http.Query

Expand All @@ -9,4 +10,7 @@ interface KeywordApi {
suspend fun getKeywords(
@Query("query") keyword: String?,
): KeywordsResponseDto

@GET("keywords/popular")
suspend fun getPopularKeywords(): PopularKeywordsResponseDto
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.into.websoso.data.remote.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class PopularKeywordsResponseDto(
@SerialName("keywords")
val keywords: List<KeywordResponseDto>,
) {
@Serializable
data class KeywordResponseDto(
@SerialName("keywordId")
val keywordId: Int,
@SerialName("keywordName")
val keywordName: String,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ class KeywordRepository
private val keywordApi: KeywordApi,
) {
suspend fun fetchKeywords(keyword: String?): CategoriesEntity = keywordApi.getKeywords(keyword).toData()

suspend fun fetchPopularKeywords(): List<CategoriesEntity.CategoryEntity.KeywordEntity> = keywordApi.getPopularKeywords().toData()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ import android.content.Intent
import android.content.Intent.ACTION_VIEW
import android.net.Uri
import android.os.Bundle
import android.view.Gravity
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo.IME_ACTION_SEARCH
import android.view.inputmethod.InputMethodManager
import android.widget.TextView
import androidx.activity.addCallback
import androidx.activity.viewModels
import com.into.websoso.R.color.gray_300_52515F
import com.into.websoso.R.drawable.bg_normal_explore_keyword_search_chip
import com.into.websoso.R.layout.activity_normal_explore
import com.into.websoso.R.style.body3
import com.into.websoso.core.common.ui.base.BaseActivity
import com.into.websoso.core.common.ui.model.CategoriesModel.CategoryModel.KeywordModel
import com.into.websoso.core.common.ui.model.ResultFrom.NormalExploreBack
import com.into.websoso.core.common.util.InfiniteScrollListener
import com.into.websoso.core.common.util.SingleEventHandler
import com.into.websoso.core.common.util.toFloatPxFromDp
import com.into.websoso.core.common.util.tracker.Tracker
import com.into.websoso.core.resource.R.string.novel_inquire_link
import com.into.websoso.databinding.ActivityNormalExploreBinding
Expand All @@ -32,6 +40,7 @@ import com.into.websoso.ui.normalExplore.model.NormalExploreUiState
import com.into.websoso.ui.novelDetail.NovelDetailActivity
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlin.math.roundToInt

@AndroidEntryPoint
class NormalExploreActivity : BaseActivity<ActivityNormalExploreBinding>(activity_normal_explore) {
Expand Down Expand Up @@ -186,6 +195,18 @@ class NormalExploreActivity : BaseActivity<ActivityNormalExploreBinding>(activit
}
}

private fun navigateToDetailExploreResult(keyword: KeywordModel) {
singleEventHandler.throttleFirst {
val intent = DetailExploreResultActivity.getIntent(
context = this,
detailExploreFilteredModel = DetailExploreFilteredModel(
keywordIds = listOf(keyword.keywordId),
),
)
startActivity(intent)
}
}

private fun navigateToNovelDetail(novelId: Long) {
singleEventHandler.throttleFirst {
val intent = NovelDetailActivity.getIntent(this, novelId)
Expand Down Expand Up @@ -239,6 +260,37 @@ class NormalExploreActivity : BaseActivity<ActivityNormalExploreBinding>(activit
normalExploreViewModel.recentSearches.observe(this) { recentSearches ->
recentSearchAdapter.submitList(recentSearches)
}

normalExploreViewModel.keywordSearches.observe(this) { keywordSearches ->
updateKeywordSearchChips(keywordSearches)
}
}

private fun updateKeywordSearchChips(keywordSearches: List<KeywordModel>) {
val keywordChipGroup = binding.wcgNormalExploreKeywordSearch
keywordChipGroup.removeAllViews()
keywordSearches.forEach { keyword ->
TextView(this@NormalExploreActivity)
.apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
KEYWORD_CHIP_HEIGHT.toFloatPxFromDp().roundToInt(),
)
text = keyword.keywordName
gravity = Gravity.CENTER
includeFontPadding = false
setTextAppearance(body3)
setTextColor(getColor(gray_300_52515F))
setBackgroundResource(bg_normal_explore_keyword_search_chip)
setPadding(
KEYWORD_CHIP_HORIZONTAL_PADDING.toFloatPxFromDp().roundToInt(),
0,
KEYWORD_CHIP_HORIZONTAL_PADDING.toFloatPxFromDp().roundToInt(),
0,
)
setOnClickListener { navigateToDetailExploreResult(keyword) }
}.also { keywordChip -> keywordChipGroup.addView(keywordChip) }
}
}

private fun updateView(uiState: NormalExploreUiState) {
Expand All @@ -264,6 +316,8 @@ class NormalExploreActivity : BaseActivity<ActivityNormalExploreBinding>(activit

companion object {
const val SEARCH_AUTHOR = "SEARCH_AUTHOR"
private const val KEYWORD_CHIP_HEIGHT = 35f
private const val KEYWORD_CHIP_HORIZONTAL_PADDING = 13f

fun getIntent(
context: Context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.into.websoso.core.common.ui.model.CategoriesModel.CategoryModel.KeywordModel
import com.into.websoso.data.model.SosoPickEntity
import com.into.websoso.data.repository.KeywordRepository
import com.into.websoso.data.repository.NovelRepository
import com.into.websoso.domain.usecase.GetNormalExploreResultUseCase
import com.into.websoso.ui.mapper.toUi
Expand All @@ -22,6 +24,7 @@ class NormalExploreViewModel
constructor(
private val getNormalExploreResultUseCase: GetNormalExploreResultUseCase,
private val novelRepository: NovelRepository,
private val keywordRepository: KeywordRepository,
private val savedStateHandle: SavedStateHandle,
) : ViewModel() {
private val _uiState: MutableLiveData<NormalExploreUiState> =
Expand Down Expand Up @@ -55,9 +58,17 @@ class NormalExploreViewModel
private val _isRecentSearchesVisible: MutableLiveData<Boolean> = MutableLiveData(false)
val isRecentSearchesVisible: LiveData<Boolean> get() = _isRecentSearchesVisible

private val _keywordSearches: MutableLiveData<List<KeywordModel>> =
MutableLiveData(emptyList())
val keywordSearches: LiveData<List<KeywordModel>> get() = _keywordSearches

private val _isKeywordSearchesVisible: MutableLiveData<Boolean> = MutableLiveData(false)
val isKeywordSearchesVisible: LiveData<Boolean> get() = _isKeywordSearchesVisible

init {
fetchSosoPicks()
fetchRecentSearches()
fetchKeywordSearches()
if (initialSearchWord.isNotBlank()) {
updateSearchResult(isSearchButtonClick = true)
}
Expand Down Expand Up @@ -90,6 +101,19 @@ class NormalExploreViewModel
}
}

private fun fetchKeywordSearches() {
viewModelScope.launch {
runCatching {
keywordRepository.fetchPopularKeywords()
}.onSuccess { result ->
_keywordSearches.value = result
.map { keyword -> keyword.toUi() }
.take(MAX_KEYWORD_SEARCH_COUNT)
updateKeywordSearchesVisibility()
}
}
}

fun updateSearchWord(searchWord: String) {
_searchWord.value = searchWord
savedStateHandle[SEARCH_AUTHOR] = searchWord
Expand All @@ -102,6 +126,7 @@ class NormalExploreViewModel
if (isSearchButtonClick) {
_isSosoPickVisible.value = false
updateRecentSearchesVisibility()
updateKeywordSearchesVisibility()
}
viewModelScope.launch {
_uiState.value = _uiState.value?.copy(loading = isSearchButtonClick)
Expand Down Expand Up @@ -149,6 +174,7 @@ class NormalExploreViewModel
_isNovelResultEmptyBoxVisibility.value = false
_isSosoPickVisible.value = true
updateRecentSearchesVisibility()
updateKeywordSearchesVisibility()
}

fun deleteRecentSearch(recentSearchId: Long) {
Expand Down Expand Up @@ -180,7 +206,13 @@ class NormalExploreViewModel
_isSosoPickVisible.value == true && _recentSearches.value.orEmpty().isNotEmpty()
}

private fun updateKeywordSearchesVisibility() {
_isKeywordSearchesVisible.value =
_isSosoPickVisible.value == true && _keywordSearches.value.orEmpty().isNotEmpty()
}

companion object {
private const val MAX_RECENT_SEARCH_COUNT = 30
private const val MAX_KEYWORD_SEARCH_COUNT = 7
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/gray_50_F4F5F8" />
<corners android:radius="20dp" />
</shape>
52 changes: 51 additions & 1 deletion app/src/main/res/layout/activity_normal_explore.xml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,56 @@
tools:listitem="@layout/item_normal_explore_genre_search" />
</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_normal_explore_keyword_search_section"
isVisible="@{normalExploreViewModel.isKeywordSearchesVisible}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_normal_explore_genre_search_section"
tools:visibility="visible">

<TextView
android:id="@+id/tv_normal_explore_keyword_search_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:text="@string/normal_explore_keyword_search_title"
android:textAppearance="@style/title2"
android:textColor="@color/black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/iv_normal_explore_keyword_search_navigate"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="3dp"
android:contentDescription="@null"
android:src="@drawable/ic_navigate_right"
app:layout_constraintBottom_toBottomOf="@id/tv_normal_explore_keyword_search_title"
app:layout_constraintStart_toEndOf="@id/tv_normal_explore_keyword_search_title"
app:layout_constraintTop_toTopOf="@id/tv_normal_explore_keyword_search_title"
app:tint="@color/black" />

<com.into.websoso.core.common.ui.custom.WebsosoChipGroup
android:id="@+id/wcg_normal_explore_keyword_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="16dp"
app:chipSpacingHorizontal="6dp"
app:chipSpacingVertical="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_normal_explore_keyword_search_title" />
</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_normal_explore_soso_pick_section"
isVisible="@{normalExploreViewModel.isSosoPickVisible}"
Expand All @@ -241,7 +291,7 @@
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_normal_explore_genre_search_section">
app:layout_constraintTop_toBottomOf="@id/cl_normal_explore_keyword_search_section">

<TextView
android:id="@+id/tv_normal_explore_soso_title"
Expand Down
1 change: 1 addition & 0 deletions core/resource/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<string name="normal_explore_recent_search_title">최근 검색어</string>
<string name="normal_explore_recent_search_delete_all">전체삭제</string>
<string name="normal_explore_genre_search_title">장르별 검색</string>
<string name="normal_explore_keyword_search_title">키워드 검색</string>

<!-- 탐색 뷰 -->
<string name="explore_normal_search_hint">작품 제목, 작가를 검색하세요</string>
Expand Down
Loading