Skip to content

Commit 60ff42c

Browse files
authored
Merge pull request #129 from rbqks529/refactor/#127_QA_5
[REFACTOR] QA 나머지 수정
2 parents 4aa6b1e + cf40ad0 commit 60ff42c

File tree

13 files changed

+233
-132
lines changed

13 files changed

+233
-132
lines changed
Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
package com.texthip.thip.ui.common.buttons
22

3+
import android.annotation.SuppressLint
34
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
46
import androidx.compose.foundation.layout.Row
57
import androidx.compose.foundation.layout.Spacer
68
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.height
710
import androidx.compose.foundation.layout.width
811
import androidx.compose.foundation.shape.RoundedCornerShape
912
import androidx.compose.runtime.Composable
13+
import androidx.compose.ui.Alignment
1014
import androidx.compose.ui.Modifier
1115
import androidx.compose.ui.draw.clip
16+
import androidx.compose.ui.platform.LocalConfiguration
1217
import androidx.compose.ui.tooling.preview.Preview
1318
import androidx.compose.ui.unit.dp
1419
import com.texthip.thip.ui.theme.ThipTheme
1520

21+
@SuppressLint("ConfigurationScreenWidthHeight")
1622
@Composable
1723
fun GenreChipRow(
1824
modifier: Modifier = Modifier.width(4.dp),
@@ -21,35 +27,95 @@ fun GenreChipRow(
2127
onSelect: (Int) -> Unit,
2228
horizontalArrangement: Arrangement.Horizontal = Arrangement.Center
2329
) {
24-
Row(
25-
modifier = Modifier.fillMaxWidth(),
26-
horizontalArrangement = horizontalArrangement
27-
) {
28-
genres.forEachIndexed { idx, genre ->
29-
OptionChipButton(
30-
modifier = Modifier
31-
.clip(RoundedCornerShape(20.dp)), // 버튼 모양에 맞게 클리핑
32-
text = genre,
33-
isFilled = true,
34-
isSelected = selectedIndex == idx,
35-
onClick = {
36-
if (selectedIndex == idx) {
37-
onSelect(-1)
38-
} else {
39-
onSelect(idx)
30+
val configuration = LocalConfiguration.current
31+
val screenWidthDp = configuration.screenWidthDp
32+
33+
if (screenWidthDp < 360) {
34+
Column(
35+
horizontalAlignment = Alignment.CenterHorizontally,
36+
modifier = Modifier.fillMaxWidth()
37+
) {
38+
Row(
39+
horizontalArrangement = Arrangement.Center
40+
) {
41+
genres.take(3).forEachIndexed { idx, genre ->
42+
OptionChipButton(
43+
modifier = Modifier
44+
.clip(RoundedCornerShape(20.dp)),
45+
text = genre,
46+
isFilled = true,
47+
isSelected = selectedIndex == idx,
48+
onClick = {
49+
if (selectedIndex == idx) {
50+
onSelect(-1)
51+
} else {
52+
onSelect(idx)
53+
}
54+
}
55+
)
56+
if (idx < 2) {
57+
Spacer(modifier = modifier)
58+
}
59+
}
60+
}
61+
Spacer(modifier = Modifier.height(8.dp))
62+
63+
Row(
64+
horizontalArrangement = Arrangement.Center
65+
) {
66+
genres.drop(3).forEachIndexed { relativeIdx, genre ->
67+
val idx = relativeIdx + 3
68+
OptionChipButton(
69+
modifier = Modifier
70+
.clip(RoundedCornerShape(20.dp)),
71+
text = genre,
72+
isFilled = true,
73+
isSelected = selectedIndex == idx,
74+
onClick = {
75+
if (selectedIndex == idx) {
76+
onSelect(-1)
77+
} else {
78+
onSelect(idx)
79+
}
80+
}
81+
)
82+
if (relativeIdx < genres.drop(3).size - 1) {
83+
Spacer(modifier = modifier)
4084
}
4185
}
42-
)
43-
if (idx < genres.size - 1) {
44-
Spacer(modifier = modifier)
86+
}
87+
}
88+
} else {
89+
Row(
90+
modifier = Modifier.fillMaxWidth(),
91+
horizontalArrangement = horizontalArrangement
92+
) {
93+
genres.forEachIndexed { idx, genre ->
94+
OptionChipButton(
95+
modifier = Modifier
96+
.clip(RoundedCornerShape(20.dp)),
97+
text = genre,
98+
isFilled = true,
99+
isSelected = selectedIndex == idx,
100+
onClick = {
101+
if (selectedIndex == idx) {
102+
onSelect(-1)
103+
} else {
104+
onSelect(idx)
105+
}
106+
}
107+
)
108+
if (idx < genres.size - 1) {
109+
Spacer(modifier = modifier)
110+
}
45111
}
46112
}
47113
}
48114
}
49115

50-
@Preview()
116+
@Preview(name = "Normal Screen (>=360dp)", widthDp = 400)
51117
@Composable
52-
fun PreviewGenreChipRow() {
118+
fun PreviewGenreChipRowNormal() {
53119
ThipTheme {
54120
GenreChipRow(
55121
genres = listOf("문학", "과학·IT", "사회과학", "인문학", "예술"),
@@ -58,3 +124,15 @@ fun PreviewGenreChipRow() {
58124
)
59125
}
60126
}
127+
128+
@Preview(name = "Small Screen (<360dp)", widthDp = 320)
129+
@Composable
130+
fun PreviewGenreChipRowSmall() {
131+
ThipTheme {
132+
GenreChipRow(
133+
genres = listOf("문학", "과학·IT", "사회과학", "인문학", "예술"),
134+
selectedIndex = 2,
135+
onSelect = {}
136+
)
137+
}
138+
}

app/src/main/java/com/texthip/thip/ui/common/cards/CardInputBook.kt

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import androidx.compose.ui.res.stringResource
2424
import androidx.compose.ui.text.style.TextOverflow
2525
import androidx.compose.ui.tooling.preview.Preview
2626
import androidx.compose.ui.unit.dp
27+
import coil.compose.AsyncImage
2728
import com.texthip.thip.R
2829
import com.texthip.thip.ui.common.buttons.OutlinedButton
2930
import com.texthip.thip.ui.theme.ThipTheme.colors
@@ -34,7 +35,9 @@ fun CardInputBook(
3435
modifier: Modifier = Modifier,
3536
title: String,
3637
author: String,
37-
imageRes: Int? = R.drawable.img_book_cover_sample, // 기본 이미지 리소스
38+
imageUrl: String? = null, // 이미지 URL (AsyncImage 사용)
39+
imageRes: Int? = R.drawable.img_book_cover_sample, // 기본 이미지 리소스 (fallback)
40+
showChangeButton: Boolean = true, // 변경 버튼 표시 여부
3841
onChangeClick: () -> Unit = {}
3942
) {
4043
Row(
@@ -47,14 +50,26 @@ fun CardInputBook(
4750
modifier = Modifier
4851
.size(width = 60.dp, height = 80.dp)
4952
) {
50-
51-
imageRes?.let {
52-
Image(
53-
painter = painterResource(id = it),
53+
if (!imageUrl.isNullOrBlank()) {
54+
// URL 이미지가 있는 경우 AsyncImage 사용
55+
AsyncImage(
56+
model = imageUrl,
5457
contentDescription = null,
5558
modifier = Modifier.fillMaxSize(),
56-
contentScale = ContentScale.Crop
59+
contentScale = ContentScale.Crop,
60+
fallback = imageRes?.let { painterResource(id = it) },
61+
error = imageRes?.let { painterResource(id = it) }
5762
)
63+
} else {
64+
// URL이 없는 경우 기본 이미지 리소스 사용
65+
imageRes?.let {
66+
Image(
67+
painter = painterResource(id = it),
68+
contentDescription = null,
69+
modifier = Modifier.fillMaxSize(),
70+
contentScale = ContentScale.Crop
71+
)
72+
}
5873
}
5974
}
6075

@@ -76,20 +91,24 @@ fun CardInputBook(
7691
Text(
7792
text = stringResource(R.string.card_input_author, author),
7893
style = typography.view_m500_s12_h20,
79-
color = colors.Grey01
94+
color = colors.Grey01,
95+
maxLines = 1,
96+
overflow = TextOverflow.Ellipsis
8097
)
8198
}
82-
// 텍스트 정보와 버튼 사이 19dp 고정 간격
83-
Spacer(modifier = Modifier.width(19.dp))
8499

85-
OutlinedButton(
86-
modifier = Modifier
87-
.size(width = 49.dp, height = 33.dp)
88-
.align(Alignment.Bottom),
89-
text = stringResource(R.string.change),
90-
textStyle = typography.view_m500_s14,
91-
onClick = onChangeClick
92-
)
100+
if (showChangeButton) {
101+
Spacer(modifier = Modifier.width(19.dp))
102+
103+
OutlinedButton(
104+
modifier = Modifier
105+
.size(width = 49.dp, height = 33.dp)
106+
.align(Alignment.Bottom),
107+
text = stringResource(R.string.change),
108+
textStyle = typography.view_m500_s14,
109+
onClick = onChangeClick
110+
)
111+
}
93112
}
94113
}
95114

@@ -105,6 +124,7 @@ fun CardInputBookPreview() {
105124
CardInputBook(
106125
title = "책제목입니다.책제목입니다.책제목입니다.책제목입니다.책제목입니다.",
107126
author = "리처드 도킨스",
127+
imageUrl = null, // 기본 이미지 사용
108128
onChangeClick = {}
109129
)
110130
}

app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,31 @@ fun MyFeedCard(
4444
onBookClick: () -> Unit = {}
4545
) {
4646
val hasImages = feedItem.imageUrls.isNotEmpty()
47-
val maxLines = if (hasImages) 3 else 8
48-
var isTextTruncated by remember { mutableStateOf(false) }
47+
val maxTextLines = if (hasImages) 3 else 8
48+
49+
// 실제 텍스트 줄 수를 기준으로 표시할 텍스트 계산
50+
val processedText = remember(feedItem.content, hasImages) {
51+
val lines = feedItem.content.split("\n")
52+
val nonEmptyLines = mutableListOf<Int>() // 실제 텍스트가 있는 줄의 인덱스
53+
54+
lines.forEachIndexed { index, line ->
55+
if (line.trim().isNotEmpty()) {
56+
nonEmptyLines.add(index)
57+
}
58+
}
59+
60+
if (nonEmptyLines.size <= maxTextLines) {
61+
// 실제 텍스트 줄이 제한보다 적으면 전체 표시
62+
feedItem.content
63+
} else {
64+
// 실제 텍스트 줄이 제한을 초과하면, maxTextLines 번째 텍스트 줄까지만 표시
65+
val lastAllowedLineIndex = nonEmptyLines[maxTextLines - 1]
66+
lines.take(lastAllowedLineIndex + 1).joinToString("\n")
67+
}
68+
}
69+
70+
// 잘림 여부는 파생 값으로 계산
71+
val isTextTruncated = processedText != feedItem.content
4972

5073
Column(
5174
modifier = modifier
@@ -99,20 +122,15 @@ fun MyFeedCard(
99122
.clickable { onContentClick() }
100123
) {
101124
Text(
102-
text = feedItem.content,
125+
text = processedText,
103126
style = typography.feedcopy_r400_s14_h20,
104127
color = colors.White,
105-
maxLines = maxLines,
106128
modifier = Modifier
107129
.fillMaxWidth()
108130
.padding(top = 16.dp),
109-
// 3. onTextLayout 콜백을 사용하여 텍스트가 잘렸는지 확인
110-
onTextLayout = { textLayoutResult ->
111-
isTextTruncated = textLayoutResult.hasVisualOverflow
112-
}
113131
)
114132

115-
// 4. 텍스트가 잘렸을 경우에만 "...더보기" 이미지를 우측 하단에 표시
133+
// 텍스트가 잘린 경우에만 "...더보기" 표시
116134
if (isTextTruncated) {
117135
Image(
118136
painter = painterResource(id = R.drawable.ic_text_more),

app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import androidx.compose.foundation.gestures.detectTapGestures
99
import androidx.compose.foundation.layout.Arrangement
1010
import androidx.compose.foundation.layout.Box
1111
import androidx.compose.foundation.layout.Column
12+
import androidx.compose.foundation.layout.ExperimentalLayoutApi
13+
import androidx.compose.foundation.layout.FlowRow
1214
import androidx.compose.foundation.layout.PaddingValues
1315
import androidx.compose.foundation.layout.Row
1416
import androidx.compose.foundation.layout.Spacer
@@ -145,6 +147,7 @@ fun FeedCommentScreen(
145147
)
146148
}
147149

150+
@OptIn(ExperimentalLayoutApi::class)
148151
@Composable
149152
private fun FeedCommentContent(
150153
modifier: Modifier = Modifier,
@@ -330,11 +333,13 @@ private fun FeedCommentContent(
330333
}
331334
}
332335
if (feedDetail.tagList.isNotEmpty()) {
333-
Row(
334-
Modifier
336+
FlowRow(
337+
modifier = Modifier
335338
.fillMaxWidth()
336339
.padding(bottom = 16.dp, start = 20.dp, end = 20.dp),
337-
horizontalArrangement = Arrangement.spacedBy(8.dp)
340+
horizontalArrangement = Arrangement.spacedBy(8.dp),
341+
verticalArrangement = Arrangement.spacedBy(8.dp),
342+
maxLines = 2 // 최대 2줄로 제한
338343
) {
339344
feedDetail.tagList.forEach { tag ->
340345
OptionChipButton(

app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,21 @@ class FeedWriteViewModel @Inject constructor(
189189
updateState { it.copy(selectedBook = book) }
190190
}
191191

192+
fun setPreselectedBookForFeed(isbn: String, bookTitle: String, bookAuthor: String, bookImageUrl: String) {
193+
val preselectedBook = BookData(
194+
title = bookTitle,
195+
imageUrl = bookImageUrl,
196+
author = bookAuthor,
197+
isbn = isbn
198+
)
199+
updateState {
200+
it.copy(
201+
selectedBook = preselectedBook,
202+
isBookPreselected = true
203+
)
204+
}
205+
}
206+
192207
fun toggleBookSearchSheet(show: Boolean) {
193208
updateState { it.copy(showBookSearchSheet = show) }
194209
if (show) {

0 commit comments

Comments
 (0)