Skip to content

Commit 0f16607

Browse files
committed
[ui]: 책 상세 페이지(피드 없음) 구현 완료 (#47)
1 parent 22f1997 commit 0f16607

2 files changed

Lines changed: 352 additions & 0 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.texthip.thip.ui.booksearch.mock
2+
3+
import com.texthip.thip.R
4+
5+
data class DetailBookData(
6+
val title: String,
7+
val author: String,
8+
val publisher: String,
9+
val description: String,
10+
val coverImageRes: Int? = R.drawable.bookcover_sample,
11+
val participantsCount: Int = 0,
12+
val recruitingRoomCount: Int = 0
13+
)
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
package com.texthip.thip.ui.booksearch.screen
2+
3+
import androidx.compose.animation.AnimatedVisibility
4+
import androidx.compose.foundation.Image
5+
import androidx.compose.foundation.background
6+
import androidx.compose.foundation.border
7+
import androidx.compose.foundation.clickable
8+
import androidx.compose.foundation.layout.Arrangement
9+
import androidx.compose.foundation.layout.Box
10+
import androidx.compose.foundation.layout.Column
11+
import androidx.compose.foundation.layout.Row
12+
import androidx.compose.foundation.layout.Spacer
13+
import androidx.compose.foundation.layout.fillMaxSize
14+
import androidx.compose.foundation.layout.fillMaxWidth
15+
import androidx.compose.foundation.layout.height
16+
import androidx.compose.foundation.layout.padding
17+
import androidx.compose.foundation.layout.size
18+
import androidx.compose.foundation.shape.RoundedCornerShape
19+
import androidx.compose.material3.Icon
20+
import androidx.compose.material3.Text
21+
import androidx.compose.runtime.Composable
22+
import androidx.compose.runtime.LaunchedEffect
23+
import androidx.compose.runtime.getValue
24+
import androidx.compose.runtime.mutableStateOf
25+
import androidx.compose.runtime.remember
26+
import androidx.compose.runtime.setValue
27+
import androidx.compose.ui.Alignment
28+
import androidx.compose.ui.Modifier
29+
import androidx.compose.ui.draw.alpha
30+
import androidx.compose.ui.draw.blur
31+
import androidx.compose.ui.graphics.Brush
32+
import androidx.compose.ui.graphics.Color
33+
import androidx.compose.ui.layout.ContentScale
34+
import androidx.compose.ui.res.painterResource
35+
import androidx.compose.ui.res.stringResource
36+
import androidx.compose.ui.text.style.TextOverflow
37+
import androidx.compose.ui.tooling.preview.Preview
38+
import androidx.compose.ui.unit.dp
39+
import com.texthip.thip.R
40+
import com.texthip.thip.ui.booksearch.mock.DetailBookData
41+
import com.texthip.thip.ui.common.buttons.ActionMediumButton
42+
import com.texthip.thip.ui.common.buttons.FilterButton
43+
import com.texthip.thip.ui.common.modal.InfoPopup
44+
import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar
45+
import com.texthip.thip.ui.common.topappbar.GradationTopAppBar
46+
import com.texthip.thip.ui.theme.ThipTheme
47+
import com.texthip.thip.ui.theme.ThipTheme.colors
48+
import com.texthip.thip.ui.theme.ThipTheme.typography
49+
import kotlinx.coroutines.delay
50+
51+
@Composable
52+
fun BookDetailScreen(
53+
modifier: Modifier = Modifier,
54+
book: DetailBookData,
55+
feedList: List<String> = emptyList(),
56+
onLeftClick: () -> Unit = {},
57+
onRightClick: () -> Unit = {},
58+
onRecruitingGroupClick: () -> Unit = {},
59+
onBookMarkClick: (Boolean) -> Unit = {},
60+
onWriteFeedClick: () -> Unit = {}
61+
) {
62+
var isAlarmVisible by remember { mutableStateOf(true) }
63+
var isIntroductionPopupVisible by remember { mutableStateOf(false) }
64+
var isBookmarked by remember { mutableStateOf(false) }
65+
66+
// 알림 5초간 노출
67+
LaunchedEffect(Unit) {
68+
isAlarmVisible = true
69+
delay(5000)
70+
isAlarmVisible = false
71+
}
72+
73+
Box(modifier = modifier.fillMaxSize()) {
74+
// 메인 컨텐츠
75+
Box(
76+
modifier = Modifier
77+
.fillMaxSize()
78+
.then(
79+
if (isIntroductionPopupVisible) {
80+
Modifier.blur(4.dp)
81+
} else {
82+
Modifier
83+
}
84+
)
85+
) {
86+
if (book.coverImageRes != null) {
87+
Box(modifier = Modifier
88+
.height(420.dp)
89+
.fillMaxWidth()) {
90+
Image(
91+
painter = painterResource(book.coverImageRes),
92+
contentDescription = null,
93+
modifier = Modifier
94+
.matchParentSize()
95+
.blur(4.dp),
96+
contentScale = ContentScale.Crop
97+
)
98+
Box(
99+
modifier = Modifier
100+
.matchParentSize()
101+
.background(
102+
brush = Brush.verticalGradient(
103+
colors = listOf(
104+
Color.Transparent,
105+
colors.Black.copy(alpha = 0.3f),
106+
colors.Black.copy(alpha = 0.6f),
107+
colors.Black.copy(alpha = 0.9f),
108+
colors.Black
109+
),
110+
startY = 0f,
111+
endY = Float.POSITIVE_INFINITY
112+
)
113+
)
114+
)
115+
}
116+
}
117+
118+
Column(modifier = Modifier.fillMaxSize()) {
119+
AnimatedVisibility(visible = isAlarmVisible) {
120+
GradationTopAppBar(
121+
isImageVisible = true,
122+
count = book.participantsCount,
123+
onLeftClick = {},
124+
onRightClick = {}
125+
)
126+
}
127+
AnimatedVisibility(visible = !isAlarmVisible) {
128+
DefaultTopAppBar(
129+
isRightIconVisible = true,
130+
isTitleVisible = false,
131+
onLeftClick = onLeftClick,
132+
onRightClick = onRightClick
133+
)
134+
}
135+
136+
Spacer(modifier = Modifier.height(8.dp))
137+
138+
// 상세 정보 영역
139+
Column(
140+
modifier = Modifier
141+
.fillMaxWidth()
142+
.padding(horizontal = 20.dp)
143+
) {
144+
Text(
145+
modifier = Modifier.padding(top = 40.dp),
146+
text = book.title,
147+
color = colors.White,
148+
style = typography.bigtitle_b700_s22
149+
)
150+
Spacer(modifier = Modifier.height(8.dp))
151+
152+
Text(
153+
text = stringResource(
154+
R.string.search_book_author,
155+
book.author,
156+
book.publisher
157+
),
158+
color = colors.Grey,
159+
style = typography.copy_r400_s12_h20
160+
)
161+
Spacer(modifier = Modifier.height(32.dp))
162+
163+
Column(
164+
modifier = Modifier
165+
.fillMaxWidth()
166+
.clickable { isIntroductionPopupVisible = true }
167+
) {
168+
Text(
169+
text = stringResource(R.string.search_book_comment),
170+
color = colors.White,
171+
style = typography.menu_r400_s14_h24,
172+
)
173+
Spacer(modifier = Modifier.height(4.dp))
174+
175+
Text(
176+
text = book.description,
177+
color = colors.Grey,
178+
style = typography.copy_r400_s12_h20,
179+
maxLines = 2,
180+
overflow = TextOverflow.Ellipsis
181+
)
182+
}
183+
184+
Spacer(modifier = Modifier.height(40.dp))
185+
Column(
186+
modifier = Modifier.fillMaxWidth(),
187+
verticalArrangement = Arrangement.spacedBy(12.dp)
188+
) {
189+
ActionMediumButton(
190+
text = stringResource(
191+
R.string.search_recruiting_group_count,
192+
book.recruitingRoomCount
193+
),
194+
contentColor = colors.Grey,
195+
backgroundColor = Color.Transparent,
196+
hasRightIcon = true,
197+
modifier = Modifier
198+
.fillMaxWidth()
199+
.border(
200+
width = 1.dp,
201+
color = colors.Grey,
202+
shape = RoundedCornerShape(12.dp)
203+
),
204+
onClick = onRecruitingGroupClick,
205+
)
206+
Row {
207+
ActionMediumButton(
208+
text = stringResource(R.string.search_write_feed_comment),
209+
contentColor = colors.White,
210+
backgroundColor = colors.Purple,
211+
hasRightIcon = true,
212+
hasRightPlusIcon = true,
213+
modifier = Modifier.weight(1f),
214+
onClick = onWriteFeedClick
215+
)
216+
Box(
217+
modifier = modifier
218+
.padding(start = 12.dp)
219+
.size(44.dp)
220+
.border(
221+
width = 1.dp,
222+
color = colors.Grey02,
223+
shape = RoundedCornerShape(12.dp)
224+
)
225+
.background(
226+
color = Color.Transparent,
227+
shape = RoundedCornerShape(12.dp)
228+
)
229+
.clickable {
230+
isBookmarked = !isBookmarked
231+
onBookMarkClick(isBookmarked)
232+
},
233+
contentAlignment = Alignment.Center,
234+
) {
235+
Icon(
236+
painter = painterResource(
237+
if (isBookmarked)
238+
R.drawable.ic_save_filled
239+
else
240+
R.drawable.ic_save
241+
),
242+
contentDescription = null,
243+
tint = Color.Unspecified,
244+
modifier = Modifier.size(24.dp)
245+
)
246+
}
247+
}
248+
}
249+
Spacer(modifier = Modifier.height(44.dp))
250+
251+
Text(
252+
text = stringResource(R.string.search_watch_feed),
253+
color = colors.Grey,
254+
style = typography.smalltitle_sb600_s18_h24,
255+
modifier = Modifier.padding(bottom = 33.dp)
256+
)
257+
258+
// 피드 리스트
259+
Spacer(
260+
modifier = Modifier
261+
.fillMaxWidth()
262+
.height(1.dp)
263+
.background(colors.DarkGrey02)
264+
)
265+
if (feedList.isEmpty()) {
266+
Box(
267+
modifier = Modifier
268+
.fillMaxWidth()
269+
.weight(1f),
270+
contentAlignment = Alignment.Center
271+
) {
272+
Column(horizontalAlignment = Alignment.CenterHorizontally) {
273+
Text(
274+
text = stringResource(R.string.search_no_feed_comment_1),
275+
color = colors.White,
276+
style = typography.smalltitle_sb600_s18_h24
277+
)
278+
Spacer(modifier = Modifier.height(6.dp))
279+
Text(
280+
text = stringResource(R.string.search_no_feed_comment_2),
281+
color = colors.Grey01,
282+
style = typography.feedcopy_r400_s14_h20
283+
)
284+
}
285+
}
286+
} else {
287+
// TODO: 피드 UI 구현 되면 수정
288+
}
289+
}
290+
}
291+
292+
// FilterButton은 블러 처리에서 제외
293+
FilterButton(
294+
modifier = Modifier
295+
.align(Alignment.TopEnd)
296+
.padding(top = 462.dp, start = 20.dp, end = 20.dp),
297+
selectedOption = stringResource(R.string.search_filter_popular),
298+
options = listOf(stringResource(R.string.search_filter_popular), stringResource(R.string.search_filter_latest)),
299+
onOptionSelected = { /* TODO: 정렬 옵션 선택시 구현 */ }
300+
)
301+
}
302+
303+
// 소개 전체보기 모달
304+
if (isIntroductionPopupVisible) {
305+
Box(
306+
modifier = Modifier
307+
.align(Alignment.Center)
308+
.padding(horizontal = 20.dp)
309+
) {
310+
InfoPopup(
311+
title = stringResource(R.string.introduction),
312+
content = book.description,
313+
onDismiss = { isIntroductionPopupVisible = false }
314+
)
315+
}
316+
}
317+
}
318+
}
319+
320+
@Preview
321+
@Composable
322+
fun PreviewBookDetailScreen() {
323+
ThipTheme {
324+
BookDetailScreen(
325+
book = DetailBookData(
326+
title = "채식주의자",
327+
author = "한강",
328+
publisher = "창비",
329+
description =
330+
"인터내셔널 북커상, 산클레멘테 문학상 수상작. 전세계가 주목한 인간의 역작을 다시 만나다.2016년 인터내셔널 북커상을 수상하며 한국문학의 입지를 한단계 확장시킨 한강의 명단소설 『채식주의자』. 15년 만에 새로운 장정과 판형으로 출간된다. 식물화로 건설해온 극단적이며 실재적인 상상력의 강렬한 결실로 고통과 구속의 피안에 존재하는 인간의 본성에 다가간 작품." +
331+
"인터내셔널 북커상, 산클레멘테 문학상 수상작. 전세계가 주목한 인간의 역작을 다시 만나다. \n\n2016년 인터내셔널 북커상을 수상하며 한국문학의 입지를 한단계 확장시킨 한강의 명단소설 『채식주의자』. 15년 만에 새로운 장정과 판형으로 출간된다. 식물화로 건설해온 극단적이며 실재적인 상상력의 강렬한 결실로 고통과 구속의 피안에 존재하는 인간의 본성에 다가간 작품.",
332+
coverImageRes = R.drawable.bookcover_sample,
333+
participantsCount = 210,
334+
recruitingRoomCount = 4
335+
),
336+
feedList = emptyList()
337+
)
338+
}
339+
}

0 commit comments

Comments
 (0)