Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. Walkthrough여러 컴포저블의 레이아웃·이미지·텍스트 처리 로직을 수정하고, FeedWriteViewModel에 도서 사전선택 메서드를 추가했으며, SearchBookDetailScreen 시그니처를 단순화하고 고객센터 URL을 교체했습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Search as SearchBookDetailScreen
participant Nav as SearchNavigation
participant FeedVM as FeedWriteViewModel
participant Write as FeedWriteScreen
User->>Search: "피드 작성" 선택
Search-->>Nav: onWriteFeedClick(bookDetail)
Nav->>FeedVM: setPreselectedBookForFeed(isbn, title, author, imageUrl)
FeedVM-->>FeedVM: selectedBook 업데이트, isBookPreselected=true
Nav->>Write: 피드 작성 화면으로 이동
Write-->>User: 사전선택 도서 표시
sequenceDiagram
participant UI as CardInputBook
participant Coil as AsyncImage
participant Res as DrawableRes
UI->>UI: imageUrl 체크
alt imageUrl 존재
UI->>Coil: AsyncImage 로드 (fallback/error = imageRes)
Coil-->>UI: 이미지 렌더링
else imageUrl 없음
UI->>Res: imageRes로 기본 이미지 렌더링
end
UI->>UI: showChangeButton에 따라 버튼 표시/숨김
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Pull Request Overview
This PR refactors various UI components and logic as part of QA fixes, focusing on improving user experience through UI adjustments and code cleanup.
- Removes unused
onRightClickparameter from search book detail screen and navigation - Updates customer service URL and improves feed text truncation logic
- Enhances book selection cards and genre chip layout for better responsive design
Reviewed Changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| SearchBookDetailScreen.kt | Removes unused onRightClick parameter |
| SearchNavigation.kt | Removes onRightClick callback implementation |
| FeedNavigation.kt | Updates book selection logic to use new preselected method |
| MypageScreen.kt | Updates customer service URL |
| SavedFeedCard.kt | Improves text truncation logic for better display |
| GroupDeadlineRoomSection.kt | Adjusts padding for better layout |
| GroupSelectBook.kt | Replaces custom book display with reusable CardInputBook component |
| FeedWriteViewModel.kt | Adds new method for preselecting books in feed |
| FeedCommentScreen.kt | Changes tag layout from Row to FlowRow for multi-line support |
| MyFeedCard.kt | Improves text truncation logic matching SavedFeedCard |
| CardInputBook.kt | Enhances component to support AsyncImage and conditional change button |
| GenreChipRow.kt | Adds responsive layout for small screens |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| val processedText = remember(feedItem.content) { | ||
| val lines = feedItem.content.split("\n") | ||
| val nonEmptyLines = mutableListOf<Int>() // 실제 텍스트가 있는 줄의 인덱스 | ||
|
|
||
| lines.forEachIndexed { index, line -> | ||
| if (line.trim().isNotEmpty()) { | ||
| nonEmptyLines.add(index) | ||
| } | ||
| } | ||
|
|
||
| if (nonEmptyLines.size <= maxTextLines) { | ||
| // 실제 텍스트 줄이 제한보다 적으면 전체 표시 | ||
| feedItem.content | ||
| } else { | ||
| // 실제 텍스트 줄이 제한을 초과하면, maxTextLines 번째 텍스트 줄까지만 표시 | ||
| val lastAllowedLineIndex = nonEmptyLines[maxTextLines - 1] | ||
| lines.take(lastAllowedLineIndex + 1).joinToString("\n") | ||
| } | ||
| } |
There was a problem hiding this comment.
This text processing logic is duplicated in MyFeedCard.kt. Consider extracting this into a shared utility function to avoid code duplication and improve maintainability.
| import androidx.compose.ui.unit.dp | ||
| import com.texthip.thip.ui.theme.ThipTheme | ||
|
|
||
| @SuppressLint("ConfigurationScreenWidthHeight") |
There was a problem hiding this comment.
Using @SuppressLint for ConfigurationScreenWidthHeight should be avoided. Consider using LocalDensity or other Compose-native approaches to handle screen size detection instead of suppressing the lint warning.
| val configuration = LocalConfiguration.current | ||
| val screenWidthDp = configuration.screenWidthDp | ||
|
|
||
| if (screenWidthDp < 360) { |
There was a problem hiding this comment.
The hardcoded value 360 should be extracted as a named constant to improve code readability and maintainability.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt (1)
24-29: modifier 파라미터를 간격(Spacer)로 재사용하는 API 설계 냄새modifier는 보통 레이아웃 수식용으로 소비자에게 열어두고, 간격은 별도 파라미터로 받는 것이 안전합니다. 현재 구현은 호출부가 modifier에 다른 연산을 추가할 경우 Spacer에도 전파되어 예기치 않은 배치를 유발할 수 있습니다.
-fun GenreChipRow( - modifier: Modifier = Modifier.width(4.dp), +import androidx.compose.ui.unit.Dp + +fun GenreChipRow( + modifier: Modifier = Modifier, + chipSpacing: Dp = 4.dp, genres: List<String>, selectedIndex: Int, onSelect: (Int) -> Unit, horizontalArrangement: Arrangement.Horizontal = Arrangement.Center ) { ... - if (idx < 2) { - Spacer(modifier = modifier) + if (idx < 2) { + Spacer(modifier = Modifier.width(chipSpacing)) } ... - if (relativeIdx < genres.drop(3).size - 1) { - Spacer(modifier = modifier) + if (relativeIdx < genres.drop(3).size - 1) { + Spacer(modifier = Modifier.width(chipSpacing)) } ... - if (idx < genres.size - 1) { - Spacer(modifier = modifier) + if (idx < genres.size - 1) { + Spacer(modifier = Modifier.width(chipSpacing)) }Also applies to: 57-58, 83-84, 109-110
app/src/main/java/com/texthip/thip/ui/common/cards/CardInputBook.kt (1)
80-81: 텍스트 영역이 지나치게 좁습니다 — weight(.1f) → 1f 권장
.weight(.1f)로 인해 제목/저자 텍스트가 버튼에 밀려 과도하게 잘릴 가능성이 큽니다. 일반적으로 가변 폭 콘텐츠에는1f를 부여해 여유 공간을 충분히 차지하도록 하는 편이 안전합니다.다음과 같이 수정 제안드립니다.
- Column( - modifier = Modifier.weight(.1f), + Column( + modifier = Modifier.weight(1f), verticalArrangement = Arrangement.Top ) {app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (1)
188-190: 사용자가 책을 다시 선택할 때 프리셀렉트 상태를 해제해야 합니다
selectBook은 현재selectedBook만 갱신하고isBookPreselected를 건드리지 않습니다. 기록장/상세에서 넘어온 프리셀렉트 상태가 남아 버튼 숨김 등 UI에 영향을 줄 수 있습니다.- fun selectBook(book: BookData) { - updateState { it.copy(selectedBook = book) } - } + fun selectBook(book: BookData) { + updateState { it.copy(selectedBook = book, isBookPreselected = false) } + }
🧹 Nitpick comments (15)
app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt (1)
195-200: 외부 URL 열기 시 예외 처리 및 콜백 일관화 제안브라우저 미설치 등으로
ActivityNotFoundException이 발생할 수 있습니다. 또한onCustomerServiceClick파라미터가 선언되어 있으나 미사용입니다. 콜백을 사용하거나 예외 처리를 추가해 안정성을 높여주세요.다음과 같이 최소 수정으로 예외를 흡수할 수 있습니다:
- onClick = { - val intent = - Intent(Intent.ACTION_VIEW, URL_CUSTOMER_SERVICE.toUri()) - context.startActivity(intent) - } + onClick = { + runCatching { + val intent = Intent(Intent.ACTION_VIEW, URL_CUSTOMER_SERVICE.toUri()) + .addCategory(Intent.CATEGORY_BROWSABLE) + context.startActivity(intent) + }.onFailure { + // TODO: 사용자에게 브라우저 없음 안내(토스트/다이얼로그) + } + }또는, 아래처럼 콜백을 사용해 화면 외부에서 열기 로직을 통일할 수도 있습니다:
- onClick = { /* open url */ } + onClick = onCustomerServiceClickapp/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt (1)
124-126: 가로 인셋 상수화로 일관성 확보제목과 컨텐츠 영역 모두
20.dp를 반복 사용합니다. 상수화하여 차후 조정 비용을 낮추고 일관성을 확보하세요.변경 예:
@@ - val horizontalPadding = sideMargin + val horizontalPadding = sideMargin + val horizontalInset = 20.dp @@ - Text( + Text( text = sectionTitle, style = typography.title_b700_s20_h24, - color = colors.White, - modifier = Modifier.padding(horizontal = 20.dp) + color = colors.White, + modifier = Modifier.padding(horizontal = horizontalInset) ) @@ - Column( + Column( verticalArrangement = Arrangement.spacedBy(20.dp), modifier = Modifier .fillMaxWidth() .height(584.dp) - .padding(horizontal = 20.dp) + .padding(horizontal = horizontalInset) ) {Also applies to: 141-142
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
336-343: 태그 2줄 래핑 구현 적합 — 숨겨진 태그에 대한 피드백(더보기/접근성) 고려
FlowRow(maxLines = 2)로 요구사항 충족됩니다. 다만 3줄 이상일 때 초과 태그가 조용히 사라집니다. 사용자가 더 있다는 사실을 인지할 수 있도록 “+N”/“더보기” 칩 추가 또는 보이스오버용semantics로 “일부 태그가 숨겨졌음” 안내를 고려해주세요.app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt (2)
50-69: 대용량 텍스트에서 불필요한 전체 스캔 최소화nonEmptyLines를 끝까지 채우지 말고 maxTextLines+1에 도달하면 조기 종료하면 비용을 줄일 수 있습니다. 현재 구현도 동작은 맞지만 긴 콘텐츠에서 불필요한 연산이 생깁니다.
135-145: 더보기 아이콘 사이즈/스타일 일관성SavedFeedCard의 더보기 아이콘과 사이즈/정렬이 다릅니다(여기선 width/height 고정). 동일 컴포넌트 간 UI 일관성을 위해 고정 사이즈 제거 또는 공통 컴포저블로 추출을 권장합니다.
- Image( + Image( painter = painterResource(id = R.drawable.ic_text_more), contentDescription = null, - modifier = Modifier - .align(Alignment.BottomEnd) - .width(80.dp) - .height(24.dp) + modifier = Modifier.align(Alignment.BottomEnd) )app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt (1)
51-73: 중복 로직 공통화 제안MyFeedCard와 동일한 잘림 로직이 중복됩니다. 재사용 가능한 유틸로 추출하면 유지보수가 쉬워집니다.
예: TextTruncation.kt
package com.texthip.thip.ui.common.text fun truncateByNonEmptyLines(content: String, maxTextLines: Int): Pair<String, Boolean> { val lines = content.split("\n") var count = 0 var lastIdx = -1 for ((i, line) in lines.withIndex()) { if (line.isNotBlank()) { count++ lastIdx = i if (count == maxTextLines) break } } if (count <= maxTextLines && lines.drop(lastIdx + 1).none { it.isNotBlank() }) { return content to false } val end = if (lastIdx >= 0) lastIdx + 1 else 0 return lines.take(end).joinToString("\n") to (content.length != lines.take(end).sumOf { it.length } + end - 1) }사용처:
val (processedText, isTextTruncated) = remember(feedItem.content, maxTextLines) { truncateByNonEmptyLines(feedItem.content, maxTextLines) }원하시면 PR에 포함해 드릴게요.
app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt (2)
30-87: 소형 화면에서 2줄 하드코딩은 스케일에 취약 — FlowRow/Wrap으로 단순화 권장장르 수가 늘면 두 번째 Row가 넘칩니다. 화면 폭 분기 + take/drop 대신 FlowRow(또는 ExperimentalLayoutApi)로 자동 줄바꿈을 사용하면 유지보수/응답성이 좋아집니다.
예시:
@OptIn(ExperimentalLayoutApi::class) @Composable fun GenreChipRow( modifier: Modifier = Modifier, chipSpacing: Dp = 4.dp, genres: List<String>, selectedIndex: Int, onSelect: (Int) -> Unit, ) { FlowRow( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(chipSpacing, Alignment.CenterHorizontally), verticalArrangement = Arrangement.spacedBy(8.dp) ) { genres.forEachIndexed { idx, genre -> OptionChipButton( text = genre, isFilled = true, isSelected = selectedIndex == idx, onClick = { onSelect(if (selectedIndex == idx) -1 else idx) } ) } } }
43-55: clip(RoundedCornerShape) 중복 적용OptionChipButton 내부에서 shape/border를 이미 처리합니다. 외부에서 clip을 또 적용하면 불필요한 레이어가 생기고 ripple 경계가 이중 관리됩니다. 제거를 권장합니다.
- OptionChipButton( - modifier = Modifier - .clip(RoundedCornerShape(20.dp)), + OptionChipButton( + modifier = Modifier, text = genre, ... - OptionChipButton( - modifier = Modifier - .clip(RoundedCornerShape(20.dp)), + OptionChipButton( + modifier = Modifier, text = genre, ... - OptionChipButton( - modifier = Modifier - .clip(RoundedCornerShape(20.dp)), + OptionChipButton( + modifier = Modifier, text = genre,Also applies to: 69-81, 95-107
app/src/main/java/com/texthip/thip/ui/common/cards/CardInputBook.kt (4)
55-62: 네트워크 이미지 로딩 UX 보완 — placeholder 추가로딩 중 깜빡임을 줄이려면
placeholder를 지정해 주는 것이 좋습니다. 현재fallback/error만 설정되어 있어 최초 로딩 시 빈 영역이 보일 수 있습니다.AsyncImage( model = imageUrl, contentDescription = null, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop, + placeholder = imageRes?.let { painterResource(id = it) }, fallback = imageRes?.let { painterResource(id = it) }, error = imageRes?.let { painterResource(id = it) } )
57-57: 접근성(a11y) 개선 — contentDescription 제공표지가 정보 전달에 유의미하다면
contentDescription을 제공해 스크린리더 사용성을 높이는 것이 좋습니다. 단순 장식이라면null유지도 가능하지만, 책 표지라면 제목 포함 설명을 권장합니다.새 문자열 리소스 추가 전제로 예시를 제안드립니다.
- contentDescription = null, + contentDescription = stringResource(R.string.cd_book_cover_with_title, title),- contentDescription = null, + contentDescription = stringResource(R.string.cd_book_cover_with_title, title),리소스 예:
cd_book_cover_with_title= "%1$s 표지".원하시면 문자열 리소스 추가 PR 스니펫도 제공하겠습니다.
Also applies to: 68-68
103-107: 버튼 고정 너비 제거로 현지화/가독성 대응고정 폭(49dp)은 언어에 따라 텍스트가 잘릴 수 있습니다. 콘텐츠 기반 너비로 전환하고 최소 폭만 제한하는 편이 안전합니다.
OutlinedButton( modifier = Modifier - .size(width = 49.dp, height = 33.dp) + .height(33.dp) + .widthIn(min = 49.dp) .align(Alignment.Bottom),
50-52: 자잘한 스타일링 제안 — 모서리 클리핑으로 일관된 썸네일 표현썸네일을 카드와 어울리게 보이도록 살짝 라운드를 주는 것을 고려해 보세요.
Box( modifier = Modifier - .size(width = 60.dp, height = 80.dp) + .size(width = 60.dp, height = 80.dp) + .clip(RoundedCornerShape(6.dp)) ) {필요 import:
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.ui.draw.clipapp/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupSelectBook.kt (1)
66-69: i18n: contentDescription 하드코딩 문자열 리소스화
"검색 아이콘"하드코딩 대신 문자열 리소스를 사용해 현지화 일관성을 유지하세요.- Icon( - painter = painterResource(id = R.drawable.ic_search), - contentDescription = "검색 아이콘", - tint = colors.Grey01 - ) + Icon( + painter = painterResource(id = R.drawable.ic_search), + contentDescription = stringResource(R.string.cd_search_icon), + tint = colors.Grey01 + )리소스 키가 없다면 추가 스니펫 제공 가능합니다.
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (2)
192-205: 중복 제거: 프리셀렉트/핀 기록/수정 데이터에서 BookData 구성 로직 통합
setPreselectedBookForFeed,setPinnedRecord,setEditData,loadFeedForEdit에 동일한BookData생성 코드가 반복됩니다. 헬퍼로 추출해 유지보수성을 높이세요.- fun setPreselectedBookForFeed(isbn: String, bookTitle: String, bookAuthor: String, bookImageUrl: String) { - val preselectedBook = BookData( - title = bookTitle, - imageUrl = bookImageUrl, - author = bookAuthor, - isbn = isbn - ) - updateState { - it.copy( - selectedBook = preselectedBook, - isBookPreselected = true - ) - } - } + fun setPreselectedBookForFeed(isbn: String, bookTitle: String, bookAuthor: String, bookImageUrl: String) { + val preselectedBook = buildBookData(isbn, bookTitle, bookAuthor, bookImageUrl) + updateState { it.copy(selectedBook = preselectedBook, isBookPreselected = true) } + }헬퍼 추가(파일 내 적절한 위치에):
private fun buildBookData(isbn: String, title: String, author: String, imageUrl: String) = BookData(title = title, imageUrl = imageUrl, author = author, isbn = isbn)그리고
setPinnedRecord/setEditData/loadFeedForEdit에서도 동일 헬퍼 사용 권장.
109-112: 하드코딩 에러 메시지 → 리소스 사용
"피드 정보를 불러올 수 없습니다."는 하드코딩되어 있습니다. 기존처럼StringResourceProvider를 사용해 i18n 일관성을 유지하세요.- it.copy( - isLoading = false, - errorMessage = "피드 정보를 불러올 수 없습니다." - ) + it.copy( + isLoading = false, + errorMessage = stringResourceProvider.getString(R.string.error_feed_detail_load_failed) + )해당 문자열 리소스가 없다면 키/값 제안드릴게요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (12)
app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/common/cards/CardInputBook.kt(5 hunks)app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupSelectBook.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/SearchNavigation.kt(0 hunks)app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt(0 hunks)
💤 Files with no reviewable changes (2)
- app/src/main/java/com/texthip/thip/ui/navigator/navigations/SearchNavigation.kt
- app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt
🧰 Additional context used
🧬 Code graph analysis (4)
app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupSelectBook.kt (1)
app/src/main/java/com/texthip/thip/ui/common/cards/CardInputBook.kt (1)
CardInputBook(33-113)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (3)
app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt (1)
updateState(47-49)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (1)
updateState(74-76)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt (1)
updateState(40-42)
app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt (1)
app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt (1)
OptionChipButton(30-93)
app/src/main/java/com/texthip/thip/ui/common/cards/CardInputBook.kt (1)
app/src/main/java/com/texthip/thip/ui/common/buttons/OutlinedButton.kt (1)
OutlinedButton(30-72)
🔇 Additional comments (8)
app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt (1)
45-45: 고객센터 URL 교체 LGTM새 링크로의 치환 자체는 문제 없습니다. 기존 인텐트 동작과의 호환성도 유지됩니다.
app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt (1)
116-116: 컨테이너 수평 패딩 제거 적절 — 내부 컴포넌트로 인셋 이관 확인 요청상위
Box의 가로 패딩 제거는 합리적입니다. 다만HorizontalPager(contentPadding = PaddingValues(horizontal = 30.dp))와cardWidth계산(양옆 30.dp) + 내부 가로 인셋(20.dp) 조합이 의도한 여백 체계인지 소형 디바이스에서 한번만 확인 부탁드립니다. 과도/이중 인셋이 없는지 UX 검증 필요합니다.app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
150-150: FlowRow 사용을 위한 Opt-in 적절 — Compose 버전 호환성만 재확인
@OptIn(ExperimentalLayoutApi::class)추가는 타당합니다. 현재 모듈의 Compose 버전이 해당 API(FlowRow의maxLines)를 지원하는지build.gradle의존성 버전을 한 번만 재확인해주세요.app/src/main/java/com/texthip/thip/ui/common/cards/CardInputBook.kt (1)
38-41: API 시그니처 변경 확인 및 호출부 검토
CardInputBook에imageUrl,imageRes,showChangeButton,onChangeClick파라미터가 추가되었습니다. 기본값 대신 의도한 값을 전달해야 하는 호출부를 확인해주세요:
- app/src/main/java/com/texthip/thip/ui/common/cards/CardInputBook.kt (프리뷰, 124행)
- app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupSelectBook.kt (78행)
app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupSelectBook.kt (1)
78-85: 중복 제거 잘했습니다 — 공용 CardInputBook 활용 👍선택 도서 표현을
CardInputBook으로 통합해 중복 레이아웃과 이미지 로딩 로직이 깔끔해졌습니다.showChangeButton = !isBookPreselected처리는 의도에 부합합니다.app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (1)
571-582: 네이밍/정합성 확인 요청 —imageUris전송 조건신규 작성에서만 이미지 추가가 가능하고 수정 모드에서는
remainImageUrls만 전달합니다. 서버가 “수정 모드에서 새 이미지 업로드 불가” 정책인지, 그리고 빈imageUris전달 시 서버가 문제 없이 처리하는지 확인 바랍니다.테스트 시나리오:
- 새 글: 이미지 3장 업로드 → 성공/갯수 제한 동작 확인
- 수정 글: 기존 2장 유지 + 1장 삭제 →
remainImageUrls만 전달되어 정상 반영되는지 확인app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (2)
154-159: 신규 API 연계 LGTM
setPreselectedBookForFeed호출로 프리셀렉트 플로우가 명확해졌습니다.bookImageUrl ?: ""처리도 NPE 방지에 적절합니다.
110-161: 이전 selectBook(BookData(...)) 호출부 제거 확인
코드베이스 전반에 해당 호출부가 더 이상 존재하지 않음을 확인했습니다.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt (1)
49-58: 이전 상태 의존성 문제 해결, 굿
remember(feedItem.content, hasImages)로 재계산 키 보강했고, 잘림 여부를 파생값으로 전환한 흐름도 좋습니다.
🧹 Nitpick comments (2)
app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt (2)
50-51: remember 키는hasImages대신maxTextLines사용 권장의도 전달과 미래 변경(줄 수 정책 변경) 안전성을 위해 직접 의존하는 파생값을 키로 두는 편이 명확합니다.
- val processedText = remember(feedItem.content, hasImages) { + val processedText = remember(feedItem.content, maxTextLines) {
133-143: 접근성: “더보기” 조작 힌트 노출이미지는 장식이므로
contentDescription = null유지해도 되지만, 컨테이너 클릭에onClickLabel을 부여하면 스크린리더 힌트가 좋아집니다. 리소스가 없다면 추가 부탁드립니다.예: Box의 clickable에 라벨 추가
- Modifier.clickable(onClickLabel = stringResource(R.string.read_more)) { onContentClick() }
리소스 키(R.string.read_more)는 프로젝트 컨벤션에 맞게 조정 필요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt(0 hunks)app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt(2 hunks)
💤 Files with no reviewable changes (1)
- app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt
| if (nonEmptyLines.size <= maxTextLines) { | ||
| // 실제 텍스트 줄이 제한보다 적으면 전체 표시 | ||
| feedItem.content | ||
| } else { | ||
| // 실제 텍스트 줄이 제한을 초과하면, maxTextLines 번째 텍스트 줄까지만 표시 | ||
| val lastAllowedLineIndex = nonEmptyLines[maxTextLines - 1] | ||
| lines.take(lastAllowedLineIndex + 1).joinToString("\n") | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
개행(‘\n’) 기준 자르기는 시각적 줄 수를 반영하지 않아 긴 단락이 통째로 노출됩니다
프리뷰의 feed1(라인 218)의 긴 단일 문단처럼 개행이 없으면 현재 로직은 전체 본문을 그대로 출력해 “…더보기”도 표시되지 않습니다. 실제 UI 줄바꿈(폭 기준)을 반영하려면 Text의 maxLines와 onTextLayout.hasVisualOverflow를 함께 사용해 시각적 줄수 기반 클램핑을 해야 합니다. 아래처럼 최소 수정으로 보강해 주세요.
적용 diff 1 (잘림 상태 계산 보강):
- // 잘림 여부는 파생 값으로 계산
- val isTextTruncated = processedText != feedItem.content
+ // 시각적 줄바꿈까지 고려한 잘림 여부 상태
+ var isTextTruncated by remember(processedText, maxTextLines) {
+ mutableStateOf(processedText != feedItem.content)
+ }적용 diff 2 (Text에 시각적 클램핑 추가):
Text(
text = processedText,
style = typography.feedcopy_r400_s14_h20,
color = colors.White,
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
+ maxLines = maxTextLines,
+ overflow = TextOverflow.Clip,
+ onTextLayout = { result ->
+ isTextTruncated = result.hasVisualOverflow || (processedText != feedItem.content)
+ }
)추가 import:
import androidx.compose.ui.text.style.TextOverflow🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt around
lines 60-67, the current logic slices content by '\n' which doesn't reflect
visual line-wrapping; replace this with a visual-line-aware clamp: keep the
existing newline-based fallback but render the preview Text with maxLines =
maxTextLines and provide onTextLayout = { if (it.hasVisualOverflow) set a flag
to indicate overflow } so you can decide whether to show the full text or the
clamped preview; add TextOverflow import and set overflow =
TextOverflow.Ellipsis (or Clip) on the Text; update the boolean/previewText
selection to prefer the visual-clamped output when onTextLayout reports
overflow, falling back to the newline-sliced string only when needed.
➕ 이슈 링크
🔎 작업 내용
📸 스크린샷
😢 해결하지 못한 과제
[] TASK
📢 리뷰어들에게
Summary by CodeRabbit
신기능
개선
기타