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

검색 결과 UI #258

Merged
merged 5 commits into from
Oct 1, 2024
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
8 changes: 7 additions & 1 deletion app/src/main/java/team/ppac/navigation/FarmemeNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import team.ppac.search.detail.navigation.navigateToSearchDetail
import team.ppac.search.detail.navigation.searchDetailScreen
import team.ppac.search.main.navigation.navigateToSearch
import team.ppac.search.main.navigation.searchScreen
import team.ppac.search.result.navigation.navigateToSearchResult
import team.ppac.search.result.navigation.searchResultScreen

@Composable
fun FarmemeNavHost(
Expand All @@ -41,13 +43,17 @@ fun FarmemeNavHost(
)
searchScreen(
analyticsHelper = analyticsHelper,
navigateToSearchDetail = { navController.navigateToSearchDetail(it) }
navigateToSearchDetail = { navController.navigateToSearchDetail(it) },
navigateToSearchResult = { navController.navigateToSearchResult() }
)
searchDetailScreen(
analyticsHelper = analyticsHelper,
navigateBack = { navController.popBackStack() },
navigateToMemeDetail = navigateToDetail
)
searchResultScreen(
navigateBack = { navController.popBackStack() },
)
myPageScreen(
analyticsHelper = analyticsHelper,
navigateToDetail = navigateToDetail,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,12 @@ fun FarmemeSearchToolbar(
modifier: Modifier = Modifier,
text: String,
onTextChanged: (String) -> Unit,
navigationIcon: (@Composable () -> Unit)? = null,
) {
Row(
modifier = modifier
.padding(
horizontal = 20.dp,
vertical = 10.dp
vertical = 16.dp
)
.statusBarsPadding()
.fillMaxWidth(),
Expand All @@ -43,7 +42,7 @@ fun FarmemeSearchToolbar(
Box(
modifier = Modifier.size(20.dp)
) {
navigationIcon?.invoke()
FarmemeIcon.Back(Modifier.size(20.dp))
}
Spacer(modifier = Modifier.size(12.dp))
FarmemeSearchTextField(
Expand Down Expand Up @@ -127,9 +126,6 @@ private fun PreviewSearchToolbar() {
FarmemeSearchToolbar(
text = text,
onTextChanged = { text = it },
navigationIcon = {
FarmemeIcon.Back()
}
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ internal fun SearchDetailScreen(
uiState.isLoading -> {
SearchDetailLoadingContent(
modifier = Modifier.fillMaxSize(),
uiState = uiState
isLoading = uiState.isLoading
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import team.ppac.common.android.util.showSkeleton
import team.ppac.designsystem.foundation.FarmemeRadius
import team.ppac.search.detail.mvi.SearchDetailUiState

@Composable
internal fun SearchDetailLoadingContent(
modifier: Modifier = Modifier,
uiState: SearchDetailUiState,
isLoading: Boolean,
) {
Column(
modifier = modifier
Expand All @@ -38,7 +37,7 @@ internal fun SearchDetailLoadingContent(
.height(18.dp)
.padding(start = 20.dp)
.clip(FarmemeRadius.Radius4.shape)
.showSkeleton(isLoading = uiState.isLoading)
.showSkeleton(isLoading = isLoading)
)
Spacer(modifier = Modifier.size(20.dp))
Row(
Expand All @@ -56,23 +55,23 @@ internal fun SearchDetailLoadingContent(
.fillMaxWidth()
.height(210.dp)
.clip(RoundedCornerShape(16.dp))
.showSkeleton(isLoading = uiState.isLoading)
.showSkeleton(isLoading = isLoading)
)
Spacer(modifier = Modifier.size(10.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.height(18.dp)
.clip(FarmemeRadius.Radius4.shape)
.showSkeleton(isLoading = uiState.isLoading)
.showSkeleton(isLoading = isLoading)
)
Spacer(modifier = Modifier.size(10.dp))
Box(
modifier = Modifier
.width(80.dp)
.height(18.dp)
.clip(FarmemeRadius.Radius4.shape)
.showSkeleton(isLoading = uiState.isLoading)
.showSkeleton(isLoading = isLoading)
)
}
}
Expand All @@ -86,7 +85,7 @@ private fun SearchDetailLoadingContentPreview() {
Box(modifier = Modifier.background(Color.White)) {
SearchDetailLoadingContent(
modifier = Modifier.fillMaxSize(),
uiState = SearchDetailUiState.INITIAL_STATE
isLoading = true
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal fun SearchRoute(
analyticsHelper: AnalyticsHelper,
viewModel: SearchViewModel = hiltViewModel(),
navigateToSearchDetail: (String) -> Unit,
navigateToSearchResult: () -> Unit,
) {
ComposableLifecycle { _, event ->
when (event) {
Expand All @@ -38,6 +39,7 @@ internal fun SearchRoute(
viewModel.sideEffect.collect { sideEffect ->
when (sideEffect) {
is SearchSideEffect.NavigateToSearchDetail -> navigateToSearchDetail(sideEffect.argument)
is SearchSideEffect.NavigateToSearchResult -> navigateToSearchResult()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class SearchViewModel @Inject constructor(
}

is SearchIntent.ClickSearch -> {
showServiceOpenDialog(true)
// showServiceOpenDialog(true)
postSideEffect(SearchSideEffect.NavigateToSearchResult)
}

is SearchIntent.ClickKeywordCard -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ internal fun FarmemeSearchBar(
Spacer(modifier = Modifier.size(12.dp))
Text(
modifier = Modifier.fillMaxWidth(),
text = "\uD83D\uDEA7 검색은 오픈 준비 중!",
text = "찾고 싶은 밈 있어?",
style = FarmemeTheme.typography.body.large.medium.copy(
color = FarmemeTheme.textColor.tertiary
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package team.ppac.search.main.mvi
import team.ppac.common.android.base.UiSideEffect

sealed class SearchSideEffect : UiSideEffect {
data class NavigateToSearchDetail(
val argument: String,
) : SearchSideEffect()
data class NavigateToSearchDetail(val argument: String) : SearchSideEffect()
data object NavigateToSearchResult : SearchSideEffect()
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ fun NavController.navigateToSearch(navOptions: NavOptions) = navigate(SEARCH_ROU
fun NavGraphBuilder.searchScreen(
analyticsHelper: AnalyticsHelper,
navigateToSearchDetail: (String) -> Unit,
navigateToSearchResult: () -> Unit,
) {
composable(
route = SEARCH_ROUTE
) {
SearchRoute(
analyticsHelper = analyticsHelper,
navigateToSearchDetail = navigateToSearchDetail
navigateToSearchDetail = navigateToSearchDetail,
navigateToSearchResult = navigateToSearchResult,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package team.ppac.search.result

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import team.ppac.common.android.base.BaseComposable

@Composable
internal fun SearchResultRoute(
modifier: Modifier = Modifier,
viewModel: SearchResultViewModel = hiltViewModel(),
) {
BaseComposable(viewModel = viewModel) { uiState ->
SearchResultScreen(
modifier = modifier,
uiState = uiState,
onQueryChange = viewModel::updateSearchQuery,
handleLoadStates = viewModel::handleLoadErrorStates,
onRetryClick = {},
onMemeClick = {},
onCopyClick = { _, _ ->},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package team.ppac.search.result

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.LayoutDirection
import androidx.paging.LoadStates
import team.ppac.common.android.component.error.FarmemeErrorScreen
import team.ppac.common.android.extension.collectPagingItemsWithHandleState
import team.ppac.designsystem.component.scaffold.FarmemeScaffold
import team.ppac.designsystem.component.tabbar.TabBarHeight
import team.ppac.designsystem.component.toolbar.FarmemeSearchToolbar
import team.ppac.search.detail.component.EmptyResultContent
import team.ppac.search.detail.component.SearchDetailLoadingContent
import team.ppac.search.detail.component.SearchDetailResultContent
import team.ppac.search.result.mvi.SearchResultUiState

@Composable
internal fun SearchResultScreen(
modifier: Modifier = Modifier,
uiState: SearchResultUiState,
handleLoadStates: (LoadStates) -> Unit,
onQueryChange: (String) -> Unit,
onRetryClick: () -> Unit,
onMemeClick: (String) -> Unit,
onCopyClick: (String, String) -> Unit,
) {
val searchResults = uiState.searchResults.collectPagingItemsWithHandleState(handleLoadStates)

when {
uiState.isError -> {
FarmemeErrorScreen(
modifier = Modifier
.fillMaxSize()
.padding(bottom = TabBarHeight),
title = "정보를 불러오지 못 했어요.\n새로고침 해주세요.",
onRetryClick = onRetryClick
)
}

else -> {
FarmemeScaffold(
modifier = modifier.fillMaxSize(),
topBar = {
FarmemeSearchToolbar(
text = uiState.query,
onTextChanged = onQueryChange
)
}
) { paddingValues ->
val innerPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
start = paddingValues.calculateStartPadding(LayoutDirection.Ltr),
end = paddingValues.calculateEndPadding(LayoutDirection.Ltr),
bottom = paddingValues.calculateBottomPadding() + TabBarHeight
)

when {
uiState.isLoading -> {
SearchDetailLoadingContent(
modifier = Modifier.fillMaxSize(),
isLoading = uiState.isLoading
)
}

else -> {
Column(
modifier = Modifier.padding(innerPadding)
) {
if (uiState.totalMemeCount == 0) {
EmptyResultContent()
} else {
SearchDetailResultContent(
totalItemCount = uiState.totalMemeCount,
searchResults = searchResults,
onMemeClick = onMemeClick,
onCopyClick = onCopyClick,
)
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package team.ppac.search.result

import androidx.lifecycle.SavedStateHandle
import androidx.paging.LoadState
import androidx.paging.LoadStates
import dagger.hilt.android.lifecycle.HiltViewModel
import team.ppac.common.android.base.BaseViewModel
import team.ppac.errorhandling.FarmemeNetworkException
import team.ppac.search.result.mvi.SearchResultIntent
import team.ppac.search.result.mvi.SearchResultSideEffect
import team.ppac.search.result.mvi.SearchResultUiState
import javax.inject.Inject

@HiltViewModel
class SearchResultViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
) : BaseViewModel<SearchResultUiState, SearchResultSideEffect, SearchResultIntent>(savedStateHandle) {

override fun createInitialState(savedStateHandle: SavedStateHandle): SearchResultUiState {
return SearchResultUiState.INITIAL_STATE
}

override suspend fun handleIntent(intent: SearchResultIntent) {
TODO("Not yet implemented")
}

override fun handleClientException(throwable: Throwable) {
TODO("Not yet implemented")
}

fun updateSearchQuery(query: String) {
reduce { copy(query = query) }
}

fun handleLoadErrorStates(loadStates: LoadStates) {
val errorLoadState = arrayOf(
loadStates.prepend,
loadStates.append,
loadStates.refresh
).filterIsInstance(LoadState.Error::class.java).firstOrNull()

val exception = errorLoadState?.error
if (exception != null) {
when (exception) {
is FarmemeNetworkException -> {
updateErrorState(isError = true)
}
}
}
}

private fun updateErrorState(isError: Boolean) {
reduce { copy(isError = isError) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package team.ppac.search.result.mvi

import team.ppac.common.android.base.UiIntent

sealed interface SearchResultIntent : UiIntent
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package team.ppac.search.result.mvi

import team.ppac.common.android.base.UiSideEffect

sealed interface SearchResultSideEffect : UiSideEffect
Loading
Loading