Skip to content

Commit c3ca075

Browse files
committed
[BOOK-479] feat: 책 검색 결과 없을 경우, 카카오톡 채널 안내 및 채팅 이동 구현
1 parent 25c1f6b commit c3ca075

File tree

5 files changed

+73
-25
lines changed

5 files changed

+73
-25
lines changed

feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class BookSearchPresenter(
6767
override fun present(): BookSearchUiState {
6868
val scope = rememberCoroutineScope()
6969
val userState by authRepository.userState.collectAsRetainedState(initial = UserState.Guest)
70-
var uiState by rememberRetained { mutableStateOf<UiState>(UiState.Idle) }
70+
var searchUiState by rememberRetained { mutableStateOf<SearchUiState>(SearchUiState.Idle) }
7171
var footerState by rememberRetained { mutableStateOf<FooterState>(FooterState.Idle) }
7272
val queryState = rememberTextFieldState()
7373
val recentSearches by repository.bookRecentSearches.collectAsRetainedState(initial = emptyList())
@@ -86,7 +86,7 @@ class BookSearchPresenter(
8686
fun searchBooks(query: String, startIndex: Int = START_INDEX) {
8787
scope.launch {
8888
if (startIndex == START_INDEX) {
89-
uiState = UiState.Loading
89+
searchUiState = SearchUiState.Loading
9090
} else {
9191
footerState = FooterState.Loading
9292
}
@@ -108,7 +108,7 @@ class BookSearchPresenter(
108108
isLastPage = result.lastPage
109109

110110
if (startIndex == START_INDEX) {
111-
uiState = UiState.Success
111+
searchUiState = SearchUiState.Success
112112
analyticsHelper.logEvent(SEARCH_BOOK_RESULT)
113113
} else {
114114
footerState = if (isLastPage) FooterState.End else FooterState.Idle
@@ -119,7 +119,7 @@ class BookSearchPresenter(
119119
analyticsHelper.logEvent(ERROR_SEARCH_LOADING)
120120
val errorMessage = exception.message ?: "알 수 없는 오류가 발생했습니다."
121121
if (startIndex == START_INDEX) {
122-
uiState = UiState.Error(exception)
122+
searchUiState = SearchUiState.Error(exception)
123123
} else {
124124
footerState = FooterState.Error(errorMessage)
125125
}
@@ -260,6 +260,10 @@ class BookSearchPresenter(
260260
is BookSearchUiEvent.OnBookRegisterSuccessCancelButtonClick -> {
261261
isBookRegisterSuccessBottomSheetVisible = false
262262
}
263+
264+
is BookSearchUiEvent.OnInquireClick -> {
265+
sideEffect = BookSearchSideEffect.NavigateToKakaoTalkChannel
266+
}
263267
}
264268
}
265269

@@ -268,7 +272,7 @@ class BookSearchPresenter(
268272
}
269273

270274
return BookSearchUiState(
271-
uiState = uiState,
275+
searchUiState = searchUiState,
272276
footerState = footerState,
273277
queryState = queryState,
274278
recentSearches = recentSearches.toImmutableList(),

feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchUi.kt

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import com.ninecraft.booket.core.common.constants.BookStatus
2424
import com.ninecraft.booket.core.common.extensions.toErrorType
2525
import com.ninecraft.booket.core.designsystem.DevicePreview
2626
import com.ninecraft.booket.core.designsystem.component.ReedDivider
27+
import com.ninecraft.booket.core.designsystem.component.button.ReedButton
28+
import com.ninecraft.booket.core.designsystem.component.button.ReedButtonColorStyle
29+
import com.ninecraft.booket.core.designsystem.component.button.smallButtonStyle
2730
import com.ninecraft.booket.core.designsystem.component.textfield.ReedTextField
2831
import com.ninecraft.booket.core.designsystem.theme.ReedTheme
2932
import com.ninecraft.booket.core.designsystem.theme.White
@@ -114,19 +117,19 @@ internal fun BookSearchContent(
114117
ReedDivider()
115118
Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing2))
116119

117-
when (state.uiState) {
118-
is UiState.Loading -> {
120+
when (state.searchUiState) {
121+
is SearchUiState.Loading -> {
119122
ReedLoadingIndicator()
120123
}
121124

122-
is UiState.Error -> {
125+
is SearchUiState.Error -> {
123126
ReedErrorUi(
124-
errorType = state.uiState.exception.toErrorType(),
127+
errorType = state.searchUiState.exception.toErrorType(),
125128
onRetryClick = { state.eventSink(BookSearchUiEvent.OnRetryClick) },
126129
)
127130
}
128131

129-
is UiState.Idle -> {
132+
is SearchUiState.Idle -> {
130133
if (state.recentSearches.isEmpty()) {
131134
Box(
132135
modifier = Modifier.fillMaxSize(),
@@ -173,17 +176,33 @@ internal fun BookSearchContent(
173176
}
174177
}
175178

176-
is UiState.Success -> {
179+
is SearchUiState.Success -> {
177180
if (state.isEmptySearchResult) {
178181
Box(
179182
modifier = Modifier.fillMaxSize(),
180183
contentAlignment = Alignment.Center,
181184
) {
182-
Text(
183-
text = stringResource(R.string.empty_results),
184-
color = ReedTheme.colors.contentSecondary,
185-
style = ReedTheme.typography.body1Medium,
186-
)
185+
Column(
186+
horizontalAlignment = Alignment.CenterHorizontally,
187+
) {
188+
Text(
189+
text = stringResource(R.string.empty_results_title),
190+
color = ReedTheme.colors.contentPrimary,
191+
style = ReedTheme.typography.headline1SemiBold,
192+
)
193+
Text(
194+
text = stringResource(R.string.empty_results_description),
195+
color = ReedTheme.colors.contentSecondary,
196+
style = ReedTheme.typography.body1Medium,
197+
)
198+
Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4))
199+
ReedButton(
200+
onClick = { state.eventSink(BookSearchUiEvent.OnInquireClick) },
201+
text = stringResource(R.string.inquire),
202+
sizeStyle = smallButtonStyle,
203+
colorStyle = ReedButtonColorStyle.SECONDARY,
204+
)
205+
}
187206
}
188207
} else {
189208
Row(
@@ -294,7 +313,7 @@ internal fun BookSearchContent(
294313

295314
@DevicePreview
296315
@Composable
297-
private fun BookSearchPreview() {
316+
private fun BookRecentSearchPreview() {
298317
ReedTheme {
299318
BookSearchUi(
300319
state = BookSearchUiState(
@@ -303,3 +322,16 @@ private fun BookSearchPreview() {
303322
)
304323
}
305324
}
325+
326+
@DevicePreview
327+
@Composable
328+
private fun BookSearchEmptyResultPreview() {
329+
ReedTheme {
330+
BookSearchContent(
331+
state = BookSearchUiState(
332+
searchUiState = SearchUiState.Success,
333+
eventSink = {},
334+
),
335+
)
336+
}
337+
}

feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchUiState.kt

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ import kotlinx.collections.immutable.persistentListOf
1414
import java.util.UUID
1515

1616
@Immutable
17-
sealed interface UiState {
18-
data object Idle : UiState
19-
data object Loading : UiState
20-
data object Success : UiState
21-
data class Error(val exception: Throwable) : UiState
17+
sealed interface SearchUiState {
18+
data object Idle : SearchUiState
19+
data object Loading : SearchUiState
20+
data object Success : SearchUiState
21+
data class Error(val exception: Throwable) : SearchUiState
2222
}
2323

2424
data class BookSearchUiState(
25-
val uiState: UiState = UiState.Idle,
25+
val searchUiState: SearchUiState = SearchUiState.Idle,
2626
val footerState: FooterState = FooterState.Idle,
2727
val queryState: TextFieldState = TextFieldState(),
2828
val recentSearches: ImmutableList<String> = persistentListOf(),
@@ -37,7 +37,7 @@ data class BookSearchUiState(
3737
val sideEffect: BookSearchSideEffect? = null,
3838
val eventSink: (BookSearchUiEvent) -> Unit,
3939
) : CircuitUiState {
40-
val isEmptySearchResult: Boolean get() = uiState is UiState.Success && searchResult.totalResults == 0
40+
val isEmptySearchResult: Boolean get() = searchUiState is SearchUiState.Success && searchResult.totalResults == 0
4141
}
4242

4343
@Immutable
@@ -46,6 +46,8 @@ sealed interface BookSearchSideEffect {
4646
val message: UiText,
4747
private val key: String = UUID.randomUUID().toString(),
4848
) : BookSearchSideEffect
49+
50+
data object NavigateToKakaoTalkChannel : BookSearchSideEffect
4951
}
5052

5153
sealed interface BookSearchUiEvent : CircuitUiEvent {
@@ -64,4 +66,5 @@ sealed interface BookSearchUiEvent : CircuitUiEvent {
6466
data object OnBookRegisterButtonClick : BookSearchUiEvent
6567
data object OnBookRegisterSuccessOkButtonClick : BookSearchUiEvent
6668
data object OnBookRegisterSuccessCancelButtonClick : BookSearchUiEvent
69+
data object OnInquireClick : BookSearchUiEvent
6770
}

feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/HandleBookSearchSideEffect.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package com.ninecraft.booket.feature.search.book
33
import android.widget.Toast
44
import androidx.compose.runtime.Composable
55
import androidx.compose.ui.platform.LocalContext
6+
import androidx.compose.ui.platform.LocalUriHandler
7+
import com.ninecraft.booket.feature.search.BuildConfig
68
import com.skydoves.compose.effects.RememberedEffect
79

810
@Composable
@@ -11,13 +13,18 @@ internal fun HandleBookSearchSideEffects(
1113
eventSink: (BookSearchUiEvent) -> Unit,
1214
) {
1315
val context = LocalContext.current
16+
val uriHandler = LocalUriHandler.current
1417

1518
RememberedEffect(state.sideEffect) {
1619
when (state.sideEffect) {
1720
is BookSearchSideEffect.ShowToast -> {
1821
Toast.makeText(context, state.sideEffect.message.asString(context), Toast.LENGTH_SHORT).show()
1922
}
2023

24+
is BookSearchSideEffect.NavigateToKakaoTalkChannel -> {
25+
uriHandler.openUri(BuildConfig.REED_KAKAOTALK_CHANNEL_URL)
26+
}
27+
2128
null -> {}
2229
}
2330

feature/search/src/main/res/values/strings.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
<string name="search_result_suffix">개</string>
66
<string name="recent_search">최근 검색어</string>
77
<string name="error_message">오류가 발생했습니다</string>
8-
<string name="empty_results">검색어와 일치하는 도서가 없습니다</string>
8+
<string name="empty_results_title">아직 등록된 책이 없어요</string>
9+
<string name="empty_results_description">카카오톡 채널로 문의를 남겨주세요</string>
10+
<string name="inquire">문의하기</string>
911
<string name="book_register_title">등록 옵션</string>
1012
<string name="book_register_success_title">도서가 등록되었어요!</string>
1113
<string name="book_register_success_description">독서 기록을 바로 시작할까요?</string>

0 commit comments

Comments
 (0)