diff --git a/common/resource/src/main/res/drawable/ic_detail_rigt.xml b/common/resource/src/main/res/drawable/ic_detail_rigt.xml
new file mode 100644
index 0000000..4154c37
--- /dev/null
+++ b/common/resource/src/main/res/drawable/ic_detail_rigt.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/common/resource/src/main/res/drawable/img_close.xml b/common/resource/src/main/res/drawable/img_close.xml
new file mode 100644
index 0000000..86107b3
--- /dev/null
+++ b/common/resource/src/main/res/drawable/img_close.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/common/resource/src/main/res/drawable/img_management.xml b/common/resource/src/main/res/drawable/img_management.xml
new file mode 100644
index 0000000..da21105
--- /dev/null
+++ b/common/resource/src/main/res/drawable/img_management.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSDialog.kt b/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSDialog.kt
new file mode 100644
index 0000000..b523085
--- /dev/null
+++ b/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSDialog.kt
@@ -0,0 +1,119 @@
+package com.idiotfrogs.designsystem.component
+
+import androidx.compose.foundation.background
+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.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.idiotfrogs.designsystem.component.button.MSButton
+import com.idiotfrogs.designsystem.theme.MSTheme
+
+@Composable
+fun MSDialog(
+ title: String,
+ content: String,
+ onConfirm: () -> Unit,
+ onCancel: () -> Unit,
+ modifier: Modifier = Modifier,
+ confirmText: String = "확인",
+ cancelText: String = "취소",
+ radius: Dp = 24.dp,
+ confirmButtonColor: Color = MSTheme.color.primaryNormal, // 기본 초록색
+ cancelButtonColor: Color = MSTheme.color.greyG1, // 기본 회색
+ confirmTextColor: Color = MSTheme.color.white,
+ cancelTextColor: Color = MSTheme.color.greyG4,
+) {
+ Dialog(onDismissRequest = onCancel) {
+ Box(modifier = modifier.fillMaxWidth()) {
+ Column(
+ modifier = Modifier
+ .clip(RoundedCornerShape(radius))
+ .background(MSTheme.color.white)
+ .padding(20.dp)
+ ) {
+ MSText(
+ text = title,
+ fontSize = 20.dp,
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ MSText(
+ text = content,
+ fontSize = 16.dp,
+ fontWeight = FontWeight.Normal
+ )
+ Spacer(modifier = Modifier.height(24.dp))
+ Row(modifier = Modifier.fillMaxWidth()) {
+ MSButton(
+ modifier = Modifier.weight(1f),
+ onClick = onCancel,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = cancelButtonColor
+ ),
+ pressColors = ButtonDefaults.buttonColors(
+ containerColor = cancelButtonColor
+ ),
+ contentPadding = PaddingValues(11.dp)
+ ) {
+ MSText(
+ text = cancelText,
+ fontSize = 16.dp,
+ color = cancelTextColor
+ )
+ }
+ Spacer(Modifier.width(8.dp))
+ MSButton(
+ modifier = Modifier.weight(1f),
+ onClick = onConfirm,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = confirmButtonColor
+ ),
+ pressColors = ButtonDefaults.buttonColors(
+ containerColor = confirmButtonColor
+ ),
+ contentPadding = PaddingValues(11.dp)
+ ) {
+ MSText(
+ text = confirmText,
+ fontSize = 16.dp,
+ color = confirmTextColor
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun MSDialogPreview() {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MSTheme.color.white)
+ ) {
+ MSDialog(
+ title = "제목",
+ content = "내용",
+ onCancel = {},
+ onConfirm = {}
+ )
+ }
+}
diff --git a/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSGalleryLayout.kt b/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSGalleryLayout.kt
new file mode 100644
index 0000000..677af7e
--- /dev/null
+++ b/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSGalleryLayout.kt
@@ -0,0 +1,126 @@
+package com.idiotfrogs.designsystem.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.blur
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.skydoves.landscapist.glide.GlideImage
+import com.idiotfrogs.resource.R
+
+@Composable
+fun MSGalleryLayout(images: List, isSeal: Boolean) {
+ Box(
+ modifier = Modifier
+ .width(if (images.size != 1) 284.dp else 142.dp)
+ .clip(RoundedCornerShape(16.dp))
+ .blur(if (isSeal) 8.dp else 0.dp)
+ ) {
+ when (images.size) {
+ 1 -> SingleImage(images[0])
+ 2 -> RowImages(images, isFirstRow = true)
+ 3 -> RowImages(images, isFirstRow = false)
+ 4 -> TwoByTwo(images)
+ else -> PatternRowsBigFirst(images)
+ }
+ }
+}
+
+@Composable
+fun SingleImage(image: String) {
+ GlideImage(
+ imageModel = { image },
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(160.dp),
+ loading = {
+ Image(
+ painter = painterResource(R.drawable.img_sample),
+ contentDescription = "Loading",
+ contentScale = ContentScale.Crop,
+ )
+ },
+ failure = {
+ Image(
+ painter = painterResource(R.drawable.img_sample),
+ contentDescription = "Failure",
+ contentScale = ContentScale.Crop,
+ )
+ }
+ )
+}
+
+@Composable
+fun RowImages(images: List, isFirstRow: Boolean) {
+ val width = when (images.size) {
+ 1 -> 284.dp
+ 2 -> 142.dp
+ 3 -> (284f / 3f).dp
+ else -> 0.dp
+ }
+ val height = if (isFirstRow) 160.dp else 107.dp
+
+ Row(modifier = Modifier.fillMaxWidth()) {
+ images.forEach { image ->
+ GlideImage(
+ imageModel = { image },
+ modifier = Modifier
+ .width(width)
+ .height(height),
+ loading = {
+ Image(
+ painter = painterResource(R.drawable.img_sample),
+ contentDescription = "Loading",
+ contentScale = ContentScale.Crop,
+ )
+ },
+ failure = {
+ Image(
+ painter = painterResource(R.drawable.img_sample),
+ contentDescription = "Failure",
+ contentScale = ContentScale.Crop,
+ )
+ }
+ )
+ }
+ }
+}
+
+@Composable
+fun TwoByTwo(images: List) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ for (row in 0 until 2) {
+ RowImages(images.subList(row * 2, row * 2 + 2), isFirstRow = false)
+ }
+ }
+}
+
+@Composable
+fun PatternRowsBigFirst(images: List) {
+ Column(
+ modifier = Modifier.fillMaxWidth()) {
+ var index = 0
+ val total = images.size
+ var rowIndex = 0
+
+ while (index < total) {
+ val remaining = total - index
+ val rowSize = when {
+ remaining == 4 -> 2
+ remaining >= 3 -> 3
+ else -> remaining
+ }
+
+ val subList = images.subList(index, index + rowSize)
+ RowImages(subList, isFirstRow = false)
+
+ index += rowSize
+ rowIndex++
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSMessageItem.kt b/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSMessageItem.kt
new file mode 100644
index 0000000..f0c663c
--- /dev/null
+++ b/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSMessageItem.kt
@@ -0,0 +1,103 @@
+package com.idiotfrogs.designsystem.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.blur
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.idiotfrogs.designsystem.theme.MSTheme
+
+@Composable
+fun MSMessageItem(
+ type: MessageType,
+ text: String = "",
+ imageList: List = emptyList(),
+ isSeal: Boolean = false,
+) {
+ if (type == MessageType.TEXT) {
+ MSText(
+ modifier = Modifier
+ .background(
+ color = MSTheme.color.primaryLight,
+ shape = RoundedCornerShape(16.dp)
+ )
+ .blur(if (isSeal) 8.dp else 0.dp)
+ .padding(16.dp),
+ text = text,
+ fontSize = 16.dp,
+ fontWeight = FontWeight.Normal
+ )
+ } else {
+ MSGalleryLayout(imageList, isSeal)
+ }
+}
+
+
+
+@Preview
+@Composable
+fun MSMessageItemPreview() {
+ val scrollState = rememberScrollState()
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(8.dp)
+ .verticalScroll(scrollState),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ MSMessageItem(
+ type = MessageType.TEXT,
+ text = "별거 아닌 대화, 별거 아닌 하루가 이렇게 기억에 남을 줄이야. 고마워, 다음에도 함께하자."
+ )
+ MSMessageItem(
+ type = MessageType.TEXT,
+ text = "별거 아닌 대화, 별거 아닌 하루가 이렇게 기억에 남을 줄이야. 고마워, 다음에도 함께하자.",
+ isSeal = true
+ )
+ MSMessageItem(
+ type = MessageType.PHOTO,
+ imageList = listOf("", "")
+ )
+ MSMessageItem(
+ type = MessageType.PHOTO,
+ isSeal = true,
+ imageList = listOf("")
+ )
+ MSMessageItem(
+ type = MessageType.PHOTO,
+ imageList = listOf("", "", "")
+ )
+ MSMessageItem(
+ type = MessageType.PHOTO,
+ isSeal = true,
+ imageList = listOf("", "", "", "")
+ )
+ MSMessageItem(
+ type = MessageType.PHOTO,
+ imageList = listOf("", "", "", "", "")
+ )
+ MSMessageItem(
+ type = MessageType.PHOTO,
+ isSeal = true,
+ imageList = listOf("", "", "", "", "", "")
+ )
+ MSMessageItem(
+ type = MessageType.PHOTO,
+ imageList = listOf("", "", "", "", "", "", "")
+ )
+ }
+}
+
+enum class MessageType {
+ TEXT, PHOTO
+}
\ No newline at end of file
diff --git a/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSText.kt b/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSText.kt
index 9afc647..108849a 100644
--- a/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSText.kt
+++ b/core/designsystem/src/main/java/com/idiotfrogs/designsystem/component/MSText.kt
@@ -5,6 +5,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
@@ -22,8 +23,8 @@ import com.idiotfrogs.resource.pretendard
@Composable
fun MSText(
text: String,
- fontSize: Dp = 14.dp, // @Preview를 위한 디폴트 값 (사용할 때는 똑같이 14.dp 여도 해당 속성은 무조건 입력하기)
modifier: Modifier = Modifier,
+ fontSize: Dp = 14.dp, // @Preview를 위한 디폴트 값 (사용할 때는 똑같이 14.dp 여도 해당 속성은 무조건 입력하기)
color: Color = MSTheme.color.black,
fontFamily: FontFamily = pretendard,
fontWeight: FontWeight = FontWeight.Bold,
@@ -56,4 +57,41 @@ fun MSText(
style = style,
modifier = modifier
)
+}
+
+@Composable
+fun MSAnnotatedText(
+ text: AnnotatedString,
+ modifier: Modifier = Modifier,
+ fontSize: Dp = 14.dp, // @Preview를 위한 디폴트 값 (사용할 때는 똑같이 14.dp 여도 해당 속성은 무조건 입력하기)
+ color: Color = MSTheme.color.black,
+ fontFamily: FontFamily = pretendard,
+ fontWeight: FontWeight = FontWeight.Bold,
+ letterSpacing: TextUnit = TextUnit.Unspecified,
+ textDecoration: TextDecoration? = null,
+ textAlign: TextAlign? = null,
+ lineHeight: TextUnit = TextUnit.Unspecified,
+ overflow: TextOverflow = TextOverflow.Clip,
+ softWrap: Boolean = true,
+ maxLines: Int = Int.MAX_VALUE,
+ minLines: Int = 1,
+ style: TextStyle = LocalTextStyle.current
+) {
+ Text(
+ text = text,
+ color = color,
+ fontSize = fontSize.toSp(),
+ fontFamily = fontFamily,
+ fontWeight = fontWeight,
+ letterSpacing = letterSpacing,
+ textDecoration = textDecoration,
+ textAlign = textAlign,
+ lineHeight = lineHeight,
+ overflow = overflow,
+ softWrap = softWrap,
+ maxLines = maxLines,
+ minLines = minLines,
+ style = style,
+ modifier = modifier
+ )
}
\ No newline at end of file
diff --git a/feature/detail/src/main/java/com/idiotfrogs/detail/DetailScreen.kt b/feature/detail/src/main/java/com/idiotfrogs/detail/DetailScreen.kt
new file mode 100644
index 0000000..11780a0
--- /dev/null
+++ b/feature/detail/src/main/java/com/idiotfrogs/detail/DetailScreen.kt
@@ -0,0 +1,340 @@
+package com.idiotfrogs.detail
+
+import androidx.compose.foundation.Image
+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.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.idiotfrogs.designsystem.component.MSAnnotatedText
+import com.idiotfrogs.designsystem.component.MSDialog
+import com.idiotfrogs.designsystem.component.MSMessageItem
+import com.idiotfrogs.designsystem.component.MSText
+import com.idiotfrogs.designsystem.component.MessageType
+import com.idiotfrogs.designsystem.component.button.MSButton
+import com.idiotfrogs.designsystem.theme.MSTheme
+import com.idiotfrogs.detail.component.MembarListItem
+import com.idiotfrogs.detail.component.RoundedProgressBar
+import com.idiotfrogs.resource.R
+
+@Composable
+fun DetailScreen(
+ title: String,
+ date: String, // TODO 추 후 날짜 로직 설계 후 변경 필요
+ isMember: Boolean,
+ isVoteStart: Boolean,
+ iSSeal: Boolean,
+ onSealClicked: () -> Unit,
+ onVoteClicked: (Boolean) -> Unit,
+ modifier: Modifier = Modifier
+) {
+ val scrollState = rememberScrollState()
+ var showVoteDialog by remember { mutableStateOf(false) }
+ val messageList = listOf("")
+
+ if (showVoteDialog) {
+ MSDialog(
+ title = "봉인 투표에 반대 하시겠습니까?",
+ content = "투표를 반대하면 투표를 다시 시작해야 합니다.",
+ onConfirm = {
+ onVoteClicked(false)
+ showVoteDialog = false
+ },
+ onCancel = { showVoteDialog = false },
+ confirmText = "반대",
+ confirmButtonColor = MSTheme.color.black
+ )
+ }
+
+ Column (
+ modifier = modifier
+ .fillMaxSize()
+ .background(MSTheme.color.bgNormal)
+ .padding(0.dp)
+ .verticalScroll(scrollState)
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(420.dp)
+ ) {
+ Image( // TODO 추 후 AsyncImage로 수정 필요
+ modifier = Modifier.matchParentSize(),
+ painter = painterResource(R.drawable.img_sample),
+ contentDescription = "Thumbnail",
+ contentScale = ContentScale.Crop
+ )
+ Image(
+ modifier = Modifier
+ .align(Alignment.TopStart)
+ .padding(top = 20.dp, start = 20.dp),
+ painter = painterResource(R.drawable.img_close),
+ contentDescription = "Close"
+ )
+ Image(
+ modifier = Modifier
+ .align(Alignment.TopEnd)
+ .padding(top = 20.dp, end = 20.dp),
+ painter = painterResource(R.drawable.img_management),
+ contentDescription = "Management"
+ )
+ MSText(
+ modifier = Modifier
+ .align(Alignment.BottomStart)
+ .padding(start = 20.dp, bottom = 38.dp),
+ text = title,
+ fontSize = 40.dp,
+ color = MSTheme.color.white
+ )
+ MSText(
+ modifier = Modifier
+ .align(Alignment.BottomStart)
+ .padding(start = 20.dp, bottom = 24.dp),
+ text = date,
+ fontSize = 12.dp,
+ color = MSTheme.color.white
+ )
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(MSTheme.color.white)
+ .padding(horizontal = 20.dp, vertical = 24.dp)
+ ) {
+ MSText(
+ text = "첫 여행, 첫 추억",
+ fontSize = 20.dp,
+ )
+ Spacer(Modifier.height(8.dp))
+ MSText(
+ text = "처음 함께 떠났던 여행의 순간들을 담은 우리의 작은 시간 저장소.",
+ fontSize = 16.dp,
+ fontWeight = FontWeight.Normal
+ )
+ if (!iSSeal) {
+ Spacer(Modifier.height(24.dp))
+ MSText(text = "티켓 봉인 투표")
+ Spacer(Modifier.height(8.dp))
+ if (isMember) {
+ if (isVoteStart) { // TODO isVoteStart 변수 관리 방법 고민 필요
+ Spacer(Modifier.height(8.dp))
+ RoundedProgressBar(0.2f)
+ Spacer(Modifier.height(24.dp))
+ Row {
+ MSButton(
+ modifier = Modifier.weight(1f),
+ onClick = { showVoteDialog = true },
+ colors = ButtonDefaults.buttonColors(
+ containerColor = MSTheme.color.greyG1
+ ),
+ contentPadding = PaddingValues(11.dp)
+ ) {
+ MSText(
+ text = "반대",
+ fontSize = 16.dp,
+ color = MSTheme.color.greyG4
+ )
+ }
+ Spacer(Modifier.width(8.dp))
+ MSButton(
+ modifier = Modifier.weight(3f),
+ onClick = { onVoteClicked(true) },
+ contentPadding = PaddingValues(11.dp)
+ ) {
+ MSText(
+ text = "찬성",
+ fontSize = 16.dp,
+ color = MSTheme.color.white
+ )
+ }
+ }
+ } else {
+ MSText(
+ text = "티켓 봉인을 봉인하려면 방장이 투표를 시작하고, 모든 맴버의 동의가 필요합니다.",
+ fontSize = 16.dp,
+ fontWeight = FontWeight.Normal
+ )
+ }
+ } else {
+ if (isVoteStart) {
+ Spacer(Modifier.height(8.dp))
+ RoundedProgressBar(0.2f)
+ Spacer(Modifier.height(24.dp))
+ Row {
+ MSButton(
+ modifier = Modifier.weight(1f),
+ onClick = { showVoteDialog = true },
+ colors = ButtonDefaults.buttonColors(
+ containerColor = MSTheme.color.greyG1
+ ),
+ contentPadding = PaddingValues(11.dp)
+ ) {
+ MSText(
+ text = "투표 취소",
+ fontSize = 16.dp,
+ color = MSTheme.color.greyG4
+ )
+ }
+ }
+ } else {
+ MSButton(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onSealClicked,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = MSTheme.color.greyG5
+ ),
+ contentPadding = PaddingValues(11.dp)
+ ) {
+ MSText(
+ text = "티켓 봉인하기",
+ fontSize = 16.dp,
+ color = MSTheme.color.white
+ )
+ }
+ }
+ }
+ }
+ }
+
+ Spacer(Modifier.height(16.dp))
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(MSTheme.color.white)
+ .padding(horizontal = 20.dp, vertical = 24.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ MSText(
+ text = "추억 메시지",
+ color = MSTheme.color.greyG4
+ )
+ Image(
+ modifier = Modifier.size(16.dp),
+ painter = painterResource(R.drawable.ic_detail_rigt),
+ contentDescription = "추억 메시지 상세 아이콘"
+ )
+ }
+ Spacer(Modifier.height(24.dp))
+ if (messageList.isEmpty()) {
+ MSButton(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = {},
+ contentPadding = PaddingValues(11.dp)
+ ) {
+ MSText(
+ text = "시작하기",
+ fontSize = 16.dp,
+ color = MSTheme.color.white
+ )
+ }
+ } else {
+ MSMessageItem(
+ type = MessageType.TEXT,
+ text = "테스트 문구 입니다.",
+ isSeal = iSSeal
+ )
+ Spacer(Modifier.height(8.dp))
+ MSMessageItem(
+ type = MessageType.PHOTO,
+ imageList = messageList,
+ isSeal = iSSeal
+ )
+ }
+ }
+
+ Spacer(Modifier.height(16.dp))
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(MSTheme.color.white)
+ .padding(horizontal = 20.dp, vertical = 24.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ MSAnnotatedText(
+ text = buildAnnotatedString {
+ append("멤버 ")
+ withStyle(
+ SpanStyle(color = MSTheme.color.primaryNormal)
+ ) { append("7") }
+ },
+ color = MSTheme.color.greyG4
+ )
+ Image(
+ modifier = Modifier.size(16.dp),
+ painter = painterResource(R.drawable.ic_detail_rigt),
+ contentDescription = "추억 메시지 상세 아이콘"
+ )
+ }
+ Spacer(Modifier.height(24.dp))
+ MembarListItem("파란 바나나 (나)", isMembar = false) // TODO 실제 본인 nickName 필요
+ Spacer(Modifier.height(16.dp))
+ HorizontalDivider(
+ thickness = 1.dp,
+ color = MSTheme.color.greyG1
+ )
+ repeat(6) { // TODO 테스트용 코드 -> 추 후 실제 list 변경 필요
+ Spacer(Modifier.height(16.dp))
+ MembarListItem(
+ nickName = when (it) {
+ 0 -> "파란 바나나"
+ 1 -> "검정 복숭아"
+ 2 -> "별 모양 파인애플"
+ 3 -> "초코 체리"
+ 4 -> "자두 수박"
+ else -> "민트 네모 수박"
+ }
+ )
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun DetailScreenPreview() {
+ DetailScreen(
+ "D-60",
+ "2025. 02. 20. (목) ~ 2025. 04. 20. (일)",
+ true,
+ true,
+ false,
+ {},
+ {}
+ )
+}
\ No newline at end of file
diff --git a/feature/detail/src/main/java/com/idiotfrogs/detail/component/MembarListItem.kt b/feature/detail/src/main/java/com/idiotfrogs/detail/component/MembarListItem.kt
new file mode 100644
index 0000000..c9dde3b
--- /dev/null
+++ b/feature/detail/src/main/java/com/idiotfrogs/detail/component/MembarListItem.kt
@@ -0,0 +1,58 @@
+package com.idiotfrogs.detail.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+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.res.painterResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import com.idiotfrogs.designsystem.component.MSText
+import com.idiotfrogs.designsystem.theme.MSTheme
+import com.idiotfrogs.resource.R
+
+@Composable
+fun MembarListItem(
+ nickName: String,
+ modifier: Modifier = Modifier,
+ isMembar: Boolean = true,
+) {
+ Row(
+ modifier = modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image( // TODO 추 후 AsyncImage 변경 필요
+ painter = painterResource(R.drawable.img_profile),
+ contentDescription = "프로필"
+ )
+ Spacer(Modifier.width(8.dp))
+ MSText(
+ text = nickName,
+ color = MSTheme.color.greyG5,
+ fontSize = 16.dp,
+ fontWeight = FontWeight.Normal,
+ )
+
+ if (!isMembar) {
+ Spacer(Modifier.weight(1f))
+ MSText(
+ modifier = Modifier
+ .background(
+ color = MSTheme.color.greyG5.copy(0.1f),
+ shape = RoundedCornerShape(6.dp)
+ )
+ .padding(5.dp),
+ text = "방장",
+ color = MSTheme.color.greyG5,
+ fontSize = 12.dp,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/detail/src/main/java/com/idiotfrogs/detail/component/RoundedProgressBar.kt b/feature/detail/src/main/java/com/idiotfrogs/detail/component/RoundedProgressBar.kt
new file mode 100644
index 0000000..dc5540a
--- /dev/null
+++ b/feature/detail/src/main/java/com/idiotfrogs/detail/component/RoundedProgressBar.kt
@@ -0,0 +1,35 @@
+package com.idiotfrogs.detail.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.unit.dp
+import com.idiotfrogs.designsystem.theme.MSTheme
+
+@Composable
+fun RoundedProgressBar(
+ progress: Float, // TODO 현재는 퍼센트로 했지만 인원수에 따른 로직 추가 필요
+ modifier: Modifier = Modifier,
+) {
+ Box(
+ modifier = modifier
+ .fillMaxWidth()
+ .height(16.dp)
+ .clip(RoundedCornerShape(4.dp))
+ .background(MSTheme.color.bgNormal)
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth(progress.coerceIn(0f, 1f))
+ .fillMaxHeight()
+ .clip(RoundedCornerShape(4.dp))
+ .background(MSTheme.color.primaryNormal)
+ )
+ }
+}