-
Notifications
You must be signed in to change notification settings - Fork 3
[REFACTOR] QA 나머지 수정 #129
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
[REFACTOR] QA 나머지 수정 #129
Changes from all commits
ba41334
e2ac9d9
14d345e
c6bef99
6e9195b
cf40ad0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,24 @@ | ||
| package com.texthip.thip.ui.common.buttons | ||
|
|
||
| import android.annotation.SuppressLint | ||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.Spacer | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.height | ||
| import androidx.compose.foundation.layout.width | ||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.ui.Alignment | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.draw.clip | ||
| import androidx.compose.ui.platform.LocalConfiguration | ||
| import androidx.compose.ui.tooling.preview.Preview | ||
| import androidx.compose.ui.unit.dp | ||
| import com.texthip.thip.ui.theme.ThipTheme | ||
|
|
||
| @SuppressLint("ConfigurationScreenWidthHeight") | ||
| @Composable | ||
| fun GenreChipRow( | ||
| modifier: Modifier = Modifier.width(4.dp), | ||
|
|
@@ -21,35 +27,95 @@ fun GenreChipRow( | |
| onSelect: (Int) -> Unit, | ||
| horizontalArrangement: Arrangement.Horizontal = Arrangement.Center | ||
| ) { | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| horizontalArrangement = horizontalArrangement | ||
| ) { | ||
| genres.forEachIndexed { idx, genre -> | ||
| OptionChipButton( | ||
| modifier = Modifier | ||
| .clip(RoundedCornerShape(20.dp)), // 버튼 모양에 맞게 클리핑 | ||
| text = genre, | ||
| isFilled = true, | ||
| isSelected = selectedIndex == idx, | ||
| onClick = { | ||
| if (selectedIndex == idx) { | ||
| onSelect(-1) | ||
| } else { | ||
| onSelect(idx) | ||
| val configuration = LocalConfiguration.current | ||
| val screenWidthDp = configuration.screenWidthDp | ||
|
|
||
| if (screenWidthDp < 360) { | ||
|
||
| Column( | ||
| horizontalAlignment = Alignment.CenterHorizontally, | ||
| modifier = Modifier.fillMaxWidth() | ||
| ) { | ||
| Row( | ||
| horizontalArrangement = Arrangement.Center | ||
| ) { | ||
| genres.take(3).forEachIndexed { idx, genre -> | ||
| OptionChipButton( | ||
| modifier = Modifier | ||
| .clip(RoundedCornerShape(20.dp)), | ||
| text = genre, | ||
| isFilled = true, | ||
| isSelected = selectedIndex == idx, | ||
| onClick = { | ||
| if (selectedIndex == idx) { | ||
| onSelect(-1) | ||
| } else { | ||
| onSelect(idx) | ||
| } | ||
| } | ||
| ) | ||
| if (idx < 2) { | ||
| Spacer(modifier = modifier) | ||
| } | ||
| } | ||
| } | ||
| Spacer(modifier = Modifier.height(8.dp)) | ||
|
|
||
| Row( | ||
| horizontalArrangement = Arrangement.Center | ||
| ) { | ||
| genres.drop(3).forEachIndexed { relativeIdx, genre -> | ||
| val idx = relativeIdx + 3 | ||
| OptionChipButton( | ||
| modifier = Modifier | ||
| .clip(RoundedCornerShape(20.dp)), | ||
| text = genre, | ||
| isFilled = true, | ||
| isSelected = selectedIndex == idx, | ||
| onClick = { | ||
| if (selectedIndex == idx) { | ||
| onSelect(-1) | ||
| } else { | ||
| onSelect(idx) | ||
| } | ||
| } | ||
| ) | ||
| if (relativeIdx < genres.drop(3).size - 1) { | ||
| Spacer(modifier = modifier) | ||
| } | ||
| } | ||
| ) | ||
| if (idx < genres.size - 1) { | ||
| Spacer(modifier = modifier) | ||
| } | ||
| } | ||
| } else { | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| horizontalArrangement = horizontalArrangement | ||
| ) { | ||
| genres.forEachIndexed { idx, genre -> | ||
| OptionChipButton( | ||
| modifier = Modifier | ||
| .clip(RoundedCornerShape(20.dp)), | ||
| text = genre, | ||
| isFilled = true, | ||
| isSelected = selectedIndex == idx, | ||
| onClick = { | ||
| if (selectedIndex == idx) { | ||
| onSelect(-1) | ||
| } else { | ||
| onSelect(idx) | ||
| } | ||
| } | ||
| ) | ||
| if (idx < genres.size - 1) { | ||
| Spacer(modifier = modifier) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Preview() | ||
| @Preview(name = "Normal Screen (>=360dp)", widthDp = 400) | ||
| @Composable | ||
| fun PreviewGenreChipRow() { | ||
| fun PreviewGenreChipRowNormal() { | ||
| ThipTheme { | ||
| GenreChipRow( | ||
| genres = listOf("문학", "과학·IT", "사회과학", "인문학", "예술"), | ||
|
|
@@ -58,3 +124,15 @@ fun PreviewGenreChipRow() { | |
| ) | ||
| } | ||
| } | ||
|
|
||
| @Preview(name = "Small Screen (<360dp)", widthDp = 320) | ||
| @Composable | ||
| fun PreviewGenreChipRowSmall() { | ||
| ThipTheme { | ||
| GenreChipRow( | ||
| genres = listOf("문학", "과학·IT", "사회과학", "인문학", "예술"), | ||
| selectedIndex = 2, | ||
| onSelect = {} | ||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,8 +44,31 @@ fun MyFeedCard( | |
| onBookClick: () -> Unit = {} | ||
| ) { | ||
| val hasImages = feedItem.imageUrls.isNotEmpty() | ||
| val maxLines = if (hasImages) 3 else 8 | ||
| var isTextTruncated by remember { mutableStateOf(false) } | ||
| val maxTextLines = if (hasImages) 3 else 8 | ||
|
|
||
| // 실제 텍스트 줄 수를 기준으로 표시할 텍스트 계산 | ||
| val processedText = remember(feedItem.content, hasImages) { | ||
| 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") | ||
| } | ||
|
Comment on lines
+60
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 개행(‘\n’) 기준 자르기는 시각적 줄 수를 반영하지 않아 긴 단락이 통째로 노출됩니다 프리뷰의 feed1(라인 218)의 긴 단일 문단처럼 개행이 없으면 현재 로직은 전체 본문을 그대로 출력해 “…더보기”도 표시되지 않습니다. 실제 UI 줄바꿈(폭 기준)을 반영하려면 적용 diff 1 (잘림 상태 계산 보강): - // 잘림 여부는 파생 값으로 계산
- val isTextTruncated = processedText != feedItem.content
+ // 시각적 줄바꿈까지 고려한 잘림 여부 상태
+ var isTextTruncated by remember(processedText, maxTextLines) {
+ mutableStateOf(processedText != feedItem.content)
+ }적용 diff 2 ( 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 |
||
| } | ||
|
|
||
| // 잘림 여부는 파생 값으로 계산 | ||
| val isTextTruncated = processedText != feedItem.content | ||
|
|
||
| Column( | ||
| modifier = modifier | ||
|
|
@@ -99,20 +122,15 @@ fun MyFeedCard( | |
| .clickable { onContentClick() } | ||
| ) { | ||
| Text( | ||
| text = feedItem.content, | ||
| text = processedText, | ||
| style = typography.feedcopy_r400_s14_h20, | ||
| color = colors.White, | ||
| maxLines = maxLines, | ||
| modifier = Modifier | ||
| .fillMaxWidth() | ||
| .padding(top = 16.dp), | ||
| // 3. onTextLayout 콜백을 사용하여 텍스트가 잘렸는지 확인 | ||
| onTextLayout = { textLayoutResult -> | ||
| isTextTruncated = textLayoutResult.hasVisualOverflow | ||
| } | ||
| ) | ||
|
|
||
| // 4. 텍스트가 잘렸을 경우에만 "...더보기" 이미지를 우측 하단에 표시 | ||
| // 텍스트가 잘린 경우에만 "...더보기" 표시 | ||
| if (isTextTruncated) { | ||
| Image( | ||
| painter = painterResource(id = R.drawable.ic_text_more), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.