Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -64,6 +64,7 @@ val Blue800 = Color(0xFF1269EC)
val Blue900 = Color(0xFF1F47CD)

val Kakao = Color(0xFFFBD300)
val HomeBg = Color(0xFFF0F9E8)

@Immutable
data class ReedColorScheme(
Expand Down Expand Up @@ -94,6 +95,7 @@ data class ReedColorScheme(

// Border
val borderPrimary: Color = Neutral200,
val borderSecondary: Color = Neutral100,
val borderBrand: Color = Green500,
val borderError: Color = Red500,

Expand Down
2 changes: 2 additions & 0 deletions feature/home/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ ksp {
dependencies {
implementations(
libs.logger,

libs.compose.system.ui.controller,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package com.ninecraft.booket.feature.home

import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import com.ninecraft.booket.feature.screens.BookDetailScreen
import com.ninecraft.booket.feature.screens.HomeScreen
import com.ninecraft.booket.feature.screens.RecordScreen
import com.ninecraft.booket.feature.screens.SearchScreen
import com.ninecraft.booket.feature.screens.SettingsScreen
import com.slack.circuit.codegen.annotations.CircuitInject
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
Expand All @@ -24,12 +26,21 @@ class HomePresenter @AssistedInject constructor(

fun handleEvent(event: HomeUiEvent) {
when (event) {
is HomeUiEvent.OnButtonClick -> {
is HomeUiEvent.OnSettingsClick -> {
navigator.goTo(SettingsScreen)
}

is HomeUiEvent.OnBookRegisterClick -> {
navigator.goTo(SearchScreen)
}

is HomeUiEvent.OnRecordButtonClick -> {
navigator.goTo(RecordScreen)
}

is HomeUiEvent.OnBookDetailClick -> {
navigator.goTo(BookDetailScreen(""))
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jul 29, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

BookDetailScreen 파라미터 확인 필요

BookDetailScreen에 빈 문자열이 전달되고 있습니다. 실제 책 ID나 데이터가 전달되어야 하는지 확인이 필요합니다.

다음 스크립트로 BookDetailScreen의 파라미터 요구사항을 확인해보세요:


🏁 Script executed:

#!/bin/bash
# BookDetailScreen의 파라미터 정의 확인
ast-grep --pattern 'data object BookDetailScreen'
ast-grep --pattern 'data class BookDetailScreen'

Length of output: 102


BookDetailScreen 선언 위치 확인을 위해 전체 코드베이스에서 해당 이름을 검색해 주세요:


🏁 Script executed:

#!/bin/bash
# 전체 코드베이스에서 BookDetailScreen 검색
rg --color=never -n "BookDetailScreen" .

Length of output: 1736


BookDetailScreen에 ISBN 파라미터 전달 필요

BookDetailScreen은 data class BookDetailScreen(val isbn: String)로 선언되어 있어, 빈 문자열이 아닌 실제 ISBN을 전달하셔야 합니다.

  • 파일:
    feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt
    라인: 42

  • 수정 제안:

    - navigator.goTo(BookDetailScreen(""))
    + navigator.goTo(BookDetailScreen(isbn = /* 선택된 책의 ISBN 변수 */))

    예를 들어, 이벤트에 ISBN이 담겨 있다면:

    - navigator.goTo(BookDetailScreen(""))
    + navigator.goTo(BookDetailScreen(isbn = event.isbn))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
navigator.goTo(BookDetailScreen(""))
- navigator.goTo(BookDetailScreen(""))
+ navigator.goTo(BookDetailScreen(isbn = event.isbn))
🤖 Prompt for AI Agents
In
feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt
at line 42, the call to navigator.goTo(BookDetailScreen("")) incorrectly passes
an empty string as the ISBN parameter. Since BookDetailScreen requires a valid
ISBN string, update this call to pass the actual ISBN value from the relevant
event or data source instead of an empty string.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

여기도 서버 연동할 때 변경사항이 생길 예정이라 그때 바꿀게

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@seoyoon513 알겠습니다! 팀에서 UI 구현 단계에서 더미 데이터를 사용하고 서버 연동 시점에 실제 데이터로 교체하는 방식을 사용하신다는 점을 고려하면, 서버 연동할 때 함께 수정하시는 것이 합리적이네요.

그때 실제 책 데이터와 함께 올바른 ISBN 파라미터를 전달하도록 수정해주시면 됩니다! 🐰

  (\   /)
 ( ._. )
o_(")(")

🧠 Learnings used
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#35
File: feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/TermsAgreementScreen.kt:127-127
Timestamp: 2025-07-09T01:14:29.836Z
Learning: In the Reed-Android project's TermsAgreementScreen.kt, the OnTermDetailClick event is intentionally passed an empty string for the URL parameter because the actual URLs for terms detail pages haven't been decided yet. This is a temporary implementation that will be updated once the URLs are finalized.

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#46
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/component/InfiniteLazyColumn.kt:83-95
Timestamp: 2025-07-14T00:46:03.843Z
Learning: seoyoon513과 팀은 한국어 주석을 선호하며, 한국어 주석을 영어로 번역하라는 제안을 하지 않아야 함

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#75
File: feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt:0-0
Timestamp: 2025-07-29T06:07:11.709Z
Learning: seoyoon513 팀에서는 UI 구현 단계에서 더미 데이터를 하드코딩하여 화면을 먼저 구현하고, 이후 서버 연동 시점에 실제 데이터로 교체하는 개발 방식을 사용합니다.

}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
package com.ninecraft.booket.feature.home

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.ninecraft.booket.core.designsystem.DevicePreview
import com.ninecraft.booket.core.designsystem.theme.HomeBg
import com.ninecraft.booket.core.designsystem.theme.ReedTheme
import com.ninecraft.booket.core.designsystem.theme.White
import com.ninecraft.booket.feature.home.component.BookCard
import com.ninecraft.booket.feature.home.component.HomeBanner
import com.ninecraft.booket.feature.home.component.HomeHeader
import com.ninecraft.booket.feature.screens.HomeScreen
import com.slack.circuit.codegen.annotations.CircuitInject
import dagger.hilt.android.components.ActivityRetainedComponent
import tech.thdev.compose.exteions.system.ui.controller.rememberSystemUiController

@CircuitInject(HomeScreen::class, ActivityRetainedComponent::class)
@Composable
Expand All @@ -25,8 +42,6 @@ internal fun HomeUi(
) {
Column(
modifier = modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
HomeContent(
state = state,
Expand All @@ -35,28 +50,90 @@ internal fun HomeUi(
}
}

@Suppress("unused")
@Composable
internal fun HomeContent(
state: HomeUiState,
modifier: Modifier = Modifier,
) {
Text(text = "홈")
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = {
state.eventSink(HomeUiEvent.OnButtonClick)
},
) {
Text(text = "도서 검색 이동")
val dummyBooks = listOf(
Book("여름은 오래 그곳에 남아", "마쓰이에 마사시", "비채", "https://image.aladin.co.kr/product/7492/9/cover200/8934972203_1.jpg", 3),
Book("여름은 오래 그곳에 남아", "마쓰이에 마사시", "비채", "https://image.aladin.co.kr/product/7492/9/cover200/8934972203_1.jpg", 3),
Book("여름은 오래 그곳에 남아", "마쓰이에 마사시", "비채", "https://image.aladin.co.kr/product/7492/9/cover200/8934972203_1.jpg", 3)
)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jul 29, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

더미 데이터 위치 개선 필요

더미 데이터가 UI 레이어에 하드코딩되어 있습니다. 이는 Presenter나 State에서 관리하는 것이 더 적절합니다.

HomeUiState에 books 필드를 추가하고 Presenter에서 더미 데이터를 제공하는 방식으로 변경을 고려해보세요.

🤖 Prompt for AI Agents
In feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt
around lines 58 to 62, the dummy book data is hardcoded directly in the UI
layer, which is not ideal. Move this dummy data out of the UI composable and
instead add a books field to the HomeUiState data class. Then, provide the dummy
book list from the Presenter or ViewModel layer by populating the books field in
HomeUiState, so the UI only consumes state data without managing the dummy data
itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

UI 보여주기 위해 하드코딩한거고 서버 연동할 때 바꿀거야

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@seoyoon513 네, 이해했습니다! UI 구현을 위한 임시 더미 데이터군요. 서버 연동 시점에 적절히 리팩토링하시면 됩니다.


✏️ Learnings added
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#75
File: feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt:0-0
Timestamp: 2025-07-29T06:07:11.709Z
Learning: seoyoon513 팀에서는 UI 구현 단계에서 더미 데이터를 하드코딩하여 화면을 먼저 구현하고, 이후 서버 연동 시점에 실제 데이터로 교체하는 개발 방식을 사용합니다.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#63
File: feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt:28-53
Timestamp: 2025-07-22T05:19:10.071Z
Learning: feature/library의 LibraryPresenter에서 현재 FilterChipState의 카운트 값들은 UI 확인용 더미 데이터이며, API가 준비되면 실제 데이터로 교체될 예정입니다.

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#46
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/component/InfiniteLazyColumn.kt:83-95
Timestamp: 2025-07-14T00:46:03.843Z
Learning: seoyoon513과 팀은 한국어 주석을 선호하며, 한국어 주석을 영어로 번역하라는 제안을 하지 않아야 함


val systemUiController = rememberSystemUiController()

DisposableEffect(systemUiController) {
systemUiController.setStatusBarColor(
color = HomeBg,
darkIcons = true,
)
onDispose {
systemUiController.setStatusBarColor(
color = White,
darkIcons = true,
)
}
}
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = {
state.eventSink(HomeUiEvent.OnRecordButtonClick)

HomeHeader(
onSettingsClick = {
state.eventSink(HomeUiEvent.OnSettingsClick)
},
modifier = modifier,
)
HomeBanner(
onBookRegisterClick = {
state.eventSink(HomeUiEvent.OnBookRegisterClick)
},
modifier = modifier,
)
Column(
modifier = Modifier
.fillMaxSize()
.background(ReedTheme.colors.baseSecondary),
) {
Text(text = "독서 기록 작성")
Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing6))
Text(
text = stringResource(R.string.home_content_label_reading_now),
modifier = Modifier.padding(start = ReedTheme.spacing.spacing5),
color = ReedTheme.colors.contentSecondary,
style = ReedTheme.typography.headline2Medium,
)
Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing3))
val pagerState = rememberPagerState(pageCount = { dummyBooks.size })
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = ReedTheme.spacing.spacing5),
pageSpacing = ReedTheme.spacing.spacing5,
) { page ->
BookCard(
bookInfo = dummyBooks[page],
onBookDetailClick = {
state.eventSink(HomeUiEvent.OnBookDetailClick)
},
onRecordButtonClick = {
state.eventSink(HomeUiEvent.OnRecordButtonClick)
}
)
}
Comment on lines +104 to +120
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jul 29, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

빈 리스트 처리 추가 필요

dummyBooks가 비어있을 경우를 처리해야 합니다. 현재 코드에서는 빈 리스트일 때 HorizontalPager가 아무것도 표시하지 않습니다.

 val pagerState = rememberPagerState(pageCount = { dummyBooks.size })
+if (dummyBooks.isEmpty()) {
+    // EmptyBookCard 표시 또는 다른 빈 상태 UI
+    return
+}
 HorizontalPager(

또는 BookCard.kt에 있는 EmptyBookCard 컴포넌트를 활용하는 방안도 고려해보세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val pagerState = rememberPagerState(pageCount = { dummyBooks.size })
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = ReedTheme.spacing.spacing5),
pageSpacing = ReedTheme.spacing.spacing5,
) { page ->
BookCard(
bookInfo = dummyBooks[page],
onBookDetailClick = {
state.eventSink(HomeUiEvent.OnBookDetailClick)
},
onRecordButtonClick = {
state.eventSink(HomeUiEvent.OnRecordButtonClick)
},
)
}
val pagerState = rememberPagerState(pageCount = { dummyBooks.size })
if (dummyBooks.isEmpty()) {
// EmptyBookCard 표시 또는 다른 빈 상태 UI
return
}
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = ReedTheme.spacing.spacing5),
pageSpacing = ReedTheme.spacing.spacing5,
) { page ->
BookCard(
bookInfo = dummyBooks[page],
onBookDetailClick = {
state.eventSink(HomeUiEvent.OnBookDetailClick)
},
onRecordButtonClick = {
state.eventSink(HomeUiEvent.OnRecordButtonClick)
},
)
}
🤖 Prompt for AI Agents
In feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt
around lines 104 to 120, the code does not handle the case when dummyBooks is
empty, causing HorizontalPager to show nothing. Add a conditional check before
rendering HorizontalPager to detect if dummyBooks is empty, and if so, display
the EmptyBookCard component from BookCard.kt instead. This ensures the UI
gracefully handles empty lists by showing a placeholder card.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

어차피 dummy 데이터로 하는거라 EmtyBookCard 적용은 API 연결 때 할 예정!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@seoyoon513 네, 이해했습니다! 더미 데이터 단계에서는 현재 구현으로 충분하고, API 연동 시점에 실제 데이터와 함께 빈 상태 처리를 구현하는 것이 합리적인 접근 방식이네요. 👍


🧠 Learnings used
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#77
File: feature/onboarding/src/main/kotlin/com/ninecraft/booket/feature/onboarding/OnboardingUi.kt:73-73
Timestamp: 2025-07-29T07:02:18.885Z
Learning: Kotlin에서 같은 패키지 내의 파일들은 패키지 레벨에 정의된 const val 상수를 import 없이 직접 접근할 수 있습니다. OnboardingPresenter.kt와 OnboardingUi.kt 모두 com.ninecraft.booket.feature.onboarding 패키지에 속해 있어서 ONBOARDING_STEPS_COUNT 상수를 공유할 수 있습니다.

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#63
File: feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt:28-53
Timestamp: 2025-07-22T05:19:10.071Z
Learning: feature/library의 LibraryPresenter에서 현재 FilterChipState의 카운트 값들은 UI 확인용 더미 데이터이며, API가 준비되면 실제 데이터로 교체될 예정입니다.

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#46
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/component/InfiniteLazyColumn.kt:83-95
Timestamp: 2025-07-14T00:46:03.843Z
Learning: seoyoon513과 팀은 한국어 주석을 선호하며, 한국어 주석을 영어로 번역하라는 제안을 하지 않아야 함

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#75
File: feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt:0-0
Timestamp: 2025-07-29T06:07:11.727Z
Learning: seoyoon513 팀에서는 UI 구현 단계에서 더미 데이터를 하드코딩하여 화면을 먼저 구현하고, 이후 서버 연동 시점에 실제 데이터로 교체하는 개발 방식을 사용합니다.

Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing5))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
repeat(pagerState.pageCount) { iteration ->
val color = if (pagerState.currentPage == iteration) ReedTheme.colors.bgPrimary else ReedTheme.colors.bgSecondaryPressed
Box(
modifier = Modifier
.size(12.dp)
.padding(3.dp)
.clip(CircleShape)
.background(color)
)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ data class HomeUiState(
) : CircuitUiState

sealed interface HomeUiEvent : CircuitUiEvent {
data object OnButtonClick : HomeUiEvent
data object OnSettingsClick : HomeUiEvent
data object OnBookRegisterClick : HomeUiEvent
data object OnRecordButtonClick : HomeUiEvent
data object OnBookDetailClick: HomeUiEvent
}

data class Book(
val title: String = "",
val author: String = "",
val publisher: String = "",
val imageUrl: String = "",
val reviewCount: Int = 0,
)
Loading
Loading