diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
deleted file mode 100644
index 00e85ee2..00000000
--- a/.idea/deploymentTargetSelector.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/bottomsheet/MenuBottomSheet.kt b/app/src/main/java/com/texthip/thip/ui/common/bottomsheet/MenuBottomSheet.kt
new file mode 100644
index 00000000..e4e5d71c
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/common/bottomsheet/MenuBottomSheet.kt
@@ -0,0 +1,179 @@
+package com.texthip.thip.ui.common.bottomsheet
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.detectVerticalDragGestures
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+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.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
+import com.texthip.thip.R
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+import kotlinx.coroutines.launch
+
+data class MenuBottomSheetItem(
+ val text: String,
+ val color: Color = Color.White,
+ val onClick: () -> Unit,
+)
+
+@Composable
+fun MenuBottomSheet(
+ items: List,
+ onDismiss: () -> Unit
+) {
+ val scope = rememberCoroutineScope()
+ val animatableOffset = remember { Animatable(300f) } // 시작 위치 아래
+ var offsetY by remember { mutableFloatStateOf(0f) }
+ var isDismissing by remember { mutableStateOf(false) }
+
+ // 등장 애니메이션
+ LaunchedEffect(Unit) {
+ animatableOffset.animateTo(
+ targetValue = 0f,
+ animationSpec = tween(durationMillis = 300)
+ )
+ }
+
+ // 바깥 클릭 감지
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ if (!isDismissing) {
+ isDismissing = true
+ scope.launch {
+ animatableOffset.animateTo(300f, tween(300))
+ onDismiss()
+ }
+ }
+ }
+ .zIndex(1f) // 다른 컴포넌트 위에 뜨도록
+ )
+
+
+ // BottomSheet 본체
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .zIndex(2f), // 시트가 배경보다 위에 뜨도록
+ contentAlignment = Alignment.BottomCenter
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .offset(y = (offsetY + animatableOffset.value).dp)
+ .background(
+ color = colors.DarkGrey,
+ shape = RoundedCornerShape(topEnd = 12.dp, topStart = 12.dp)
+ )
+ .padding(20.dp)
+ .pointerInput(Unit) {
+ detectVerticalDragGestures(
+ onVerticalDrag = { _, dragAmount ->
+ if (dragAmount > 0) {
+ offsetY += dragAmount / 2 // 아래로 드래그할 때만 적용
+ }
+ },
+ onDragEnd = {
+ if (offsetY > 100f && !isDismissing) {
+ isDismissing = true
+ scope.launch {
+ animatableOffset.animateTo(300f, tween(300))
+ onDismiss()
+ }
+ } else {
+ offsetY = 0f
+ }
+ }
+ )
+ }
+ .clickable(enabled = true) {} // 내부 클릭 무시되지 않도록
+ ) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ items.forEachIndexed { index, item ->
+ if (index > 0) {
+ Spacer(modifier = Modifier.height(8.dp))
+ HorizontalDivider(modifier = Modifier.height(1.dp), color = colors.Grey03)
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+
+ Column(
+ modifier = Modifier
+ .height(50.dp)
+ .padding(horizontal = 12.dp, vertical = 8.dp),
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = item.text,
+ style = typography.menu_m500_s16_h24,
+ color = item.color,
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ item.onClick()
+ onDismiss()
+ }
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun MenuBottomSheetPreview() {
+ MenuBottomSheet(
+ items = listOf(
+ MenuBottomSheetItem(
+ text = stringResource(R.string.leave_room),
+ color = colors.White,
+ onClick = { }
+ ),
+ MenuBottomSheetItem(
+ text = stringResource(R.string.report_room),
+ color = colors.Red,
+ onClick = { }
+ ),
+ MenuBottomSheetItem(
+ text = stringResource(R.string.delete_room),
+ color = colors.Red,
+ onClick = { }
+ )
+ ),
+ onDismiss = {}
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt
new file mode 100644
index 00000000..60a64898
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt
@@ -0,0 +1,119 @@
+package com.texthip.thip.ui.common.buttons
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+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.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.R
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun ActionBarButton(
+ modifier: Modifier = Modifier,
+ isLiked: Boolean,
+ likeCount: Int,
+ commentCount: Int,
+ isSaveVisible: Boolean = false,
+ isSaved: Boolean = false,
+ isPinVisible: Boolean = false,
+ onLikeClick: () -> Unit = {},
+ onCommentClick: () -> Unit = {},
+ onBookmarkClick: () -> Unit = {},
+ onPinClick: () -> Unit = {}
+) {
+ Row(
+ modifier = modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ Row(
+ modifier = Modifier.clickable { onLikeClick() },
+ horizontalArrangement = Arrangement.spacedBy(2.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(if (isLiked) R.drawable.ic_heart_filled else R.drawable.ic_heart),
+ contentDescription = null,
+ tint = Color.Unspecified
+ )
+ Text(
+ text = likeCount.toString(),
+ style = typography.feedcopy_r400_s14_h20,
+ color = colors.White,
+ )
+ }
+
+ Row(
+ modifier = Modifier.clickable { onCommentClick() },
+ horizontalArrangement = Arrangement.spacedBy(2.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.ic_comment),
+ contentDescription = null,
+ tint = colors.White
+ )
+ Text(
+ text = commentCount.toString(),
+ style = typography.feedcopy_r400_s14_h20,
+ color = colors.White,
+ )
+ }
+
+ if (isPinVisible) {
+ Icon(
+ modifier = Modifier.clickable { onPinClick() },
+ painter = painterResource(R.drawable.ic_pin),
+ contentDescription = null,
+ tint = Color.White
+ )
+ }
+ }
+
+ if (isSaveVisible) {
+ Icon(
+ modifier = Modifier.clickable { onBookmarkClick() },
+ painter = painterResource(if (isSaved) R.drawable.ic_save_filled else R.drawable.ic_save),
+ contentDescription = null,
+ tint = Color.Unspecified
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun ActionBarButtonPreview() {
+ var isLiked by remember { mutableStateOf(false) }
+ var isSaved by remember { mutableStateOf(false) }
+
+ ActionBarButton(
+ isLiked = isLiked,
+ likeCount = 123,
+ commentCount = 45,
+ isSaveVisible = true,
+ isSaved = isSaved,
+ isPinVisible = true,
+ onLikeClick = { isLiked = !isLiked },
+ onCommentClick = {},
+ onBookmarkClick = { isSaved = !isSaved },
+ onPinClick = {}
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/FilterButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/FilterButton.kt
index af36e038..bb5e1710 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/buttons/FilterButton.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/FilterButton.kt
@@ -42,10 +42,12 @@ fun FilterButton(
) {
var expanded by remember { mutableStateOf(false) }
- Column(modifier = modifier) {
+ Column(
+ modifier = modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.End
+ ) {
Row(
modifier = Modifier
- .fillMaxWidth()
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null // 클릭 효과 제거
diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/FilterChipButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/FilterChipButton.kt
new file mode 100644
index 00000000..06664188
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/FilterChipButton.kt
@@ -0,0 +1,79 @@
+package com.texthip.thip.ui.common.buttons
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+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.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.R
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun FilterChipButton(
+ modifier: Modifier = Modifier,
+ text: String,
+ isSelected: Boolean,
+ onClick: () -> Unit,
+ onCloseClick: () -> Unit
+) {
+ Row(
+ modifier = modifier
+ .height(36.dp)
+ .background(
+ color = if (isSelected) colors.Purple else colors.DarkGrey,
+ shape = RoundedCornerShape(20.dp)
+ )
+ .padding(start = 12.dp, end = if (isSelected) 8.dp else 12.dp)
+ .clickable(
+ interactionSource = remember { MutableInteractionSource() },
+ indication = null // 클릭 효과 제거
+ ) {
+ onClick()
+ },
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Text(
+ text = text,
+ style = typography.menu_r400_s14_h24,
+ color = colors.White
+ )
+
+ if (isSelected) {
+ Icon(
+ painter = painterResource(R.drawable.ic_x),
+ contentDescription = "Close Icon",
+ tint = colors.White,
+ modifier = Modifier.clickable(onClick = onCloseClick)
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun FilterChipButtonPreview() {
+ var isSelected by remember { mutableStateOf(false) }
+ FilterChipButton(
+ text = "페이지별 보기",
+ isSelected = isSelected,
+ onClick = { isSelected = !isSelected },
+ onCloseClick = { isSelected = false }
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/FormButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/FormButton.kt
new file mode 100644
index 00000000..8d5f2b33
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/FormButton.kt
@@ -0,0 +1,101 @@
+package com.texthip.thip.ui.common.buttons
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.R
+import com.texthip.thip.ui.common.forms.PageTextField
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun FormButton(
+ modifier: Modifier = Modifier,
+ firstPage: String,
+ onFirstPageChange: (String) -> Unit,
+ lastPage: String,
+ onLastPageChange: (String) -> Unit,
+ onFinishClick: () -> Unit
+) {
+ val isSendEnabled = firstPage.isNotBlank() || lastPage.isNotBlank()
+
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .height(36.dp)
+ .background(color = colors.DarkGrey, shape = RoundedCornerShape(20.dp))
+ .padding(start = 12.dp, top = 4.dp, end = 4.dp, bottom = 4.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ PageTextField(
+ text = firstPage,
+ onTextChange = onFirstPageChange
+ )
+
+ Text(
+ text = stringResource(R.string.tilde),
+ style = typography.copy_r400_s14,
+ color = colors.White
+ )
+
+ PageTextField(
+ text = lastPage,
+ onTextChange = onLastPageChange
+ )
+
+ Text(
+ text = stringResource(R.string.page),
+ style = typography.copy_r400_s14,
+ color = colors.White
+ )
+
+ SendButton(
+ icon = R.drawable.ic_reset,
+ enabled = isSendEnabled
+ ) {
+ onFirstPageChange("")
+ onLastPageChange("")
+ }
+ }
+
+ SendButton(
+ icon = R.drawable.ic_check,
+ onClick = onFinishClick
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun FormButtonPreview() {
+ var firstPage by rememberSaveable { mutableStateOf("") }
+ var lastPage by rememberSaveable { mutableStateOf("") }
+
+ FormButton(
+ firstPage = firstPage,
+ onFirstPageChange = { firstPage = it },
+ lastPage = lastPage,
+ onLastPageChange = { lastPage = it },
+ onFinishClick = {}
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt
index 1e492a6e..a631aebf 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
@@ -19,6 +20,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.texthip.thip.R
@@ -31,17 +33,21 @@ fun OptionChipButton(
text: String,
isFilled: Boolean = false,
isSelected: Boolean? = null, // 추가
+ enabled: Boolean = true,
+ textStyle: TextStyle = typography.info_r400_s12,
onClick: () -> Unit,
) {
var isClicked by remember { mutableStateOf(false) }
val checked = isSelected ?: isClicked
val textColor = when {
+ !enabled && isFilled -> colors.Grey02
isFilled -> colors.White
checked -> colors.Purple
else -> colors.Grey01
}
val backgroundColor = when {
+ !enabled && isFilled -> colors.DarkGrey02
isFilled && checked -> colors.Purple
isFilled -> colors.DarkGrey
else -> Color.Transparent
@@ -63,16 +69,17 @@ fun OptionChipButton(
color = borderColor,
shape = RoundedCornerShape(20.dp)
)
- .clickable {
+ .clickable(enabled = enabled) {
if (isSelected == null) isClicked = !isClicked
onClick()
}
- .padding(vertical = 8.dp, horizontal = 12.dp),
+ .height(30.dp)
+ .padding(horizontal = 12.dp),
contentAlignment = Alignment.Center,
) {
Text(
text = text,
- style = typography.info_r400_s12,
+ style = textStyle,
color = textColor
)
}
diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/SendButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/SendButton.kt
new file mode 100644
index 00000000..b238d471
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/SendButton.kt
@@ -0,0 +1,69 @@
+package com.texthip.thip.ui.common.buttons
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+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.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.R
+import com.texthip.thip.ui.theme.ThipTheme.colors
+
+@Composable
+fun SendButton(
+ modifier: Modifier = Modifier,
+ icon: Int,
+ enabled: Boolean = true,
+ onClick: () -> Unit = {}
+) {
+ Box(
+ modifier = modifier
+ .background(
+ color = if (enabled) colors.Purple else colors.Grey02,
+ shape = RoundedCornerShape(20.dp)
+ )
+ .let {
+ if (enabled) it.clickable(onClick = onClick) else it
+ }
+ .padding(vertical = 2.dp, horizontal = 9.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ Icon(
+ painter = painterResource(id = icon),
+ contentDescription = "Send Icon",
+ tint = colors.White,
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun SendButtonPreview() {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ // 비활성 상태
+ SendButton(
+ icon = R.drawable.ic_send,
+ enabled = false,
+ onClick = { }
+ )
+
+ // 활성 상태
+ SendButton(
+ icon = R.drawable.ic_send,
+ onClick = { }
+ )
+ }
+}
diff --git a/app/src/main/java/com/texthip/thip/ui/common/forms/PageTextField.kt b/app/src/main/java/com/texthip/thip/ui/common/forms/PageTextField.kt
new file mode 100644
index 00000000..f2806710
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/common/forms/PageTextField.kt
@@ -0,0 +1,59 @@
+package com.texthip.thip.ui.common.forms
+
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun PageTextField(
+ modifier: Modifier = Modifier,
+ text: String,
+ onTextChange: (String) -> Unit,
+) {
+ Box(
+ modifier = modifier
+ .size(width = 36.dp, height = 26.dp)
+ .border(width = 1.dp, color = colors.White, shape = RoundedCornerShape(8.dp))
+ .padding(horizontal = 4.dp),
+ contentAlignment = Alignment.CenterStart,
+ ) {
+ BasicTextField(
+ value = text,
+ onValueChange = onTextChange,
+ textStyle = typography.copy_r400_s14.copy(color = colors.White),
+ keyboardOptions = KeyboardOptions.Default.copy(
+ keyboardType = KeyboardType.Number
+ ),
+ cursorBrush = SolidColor(colors.NeonGreen),
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun PageTextFieldPreview() {
+ var text by rememberSaveable { mutableStateOf("") }
+
+ PageTextField(
+ text = text,
+ onTextChange = { text = it },
+ modifier = Modifier
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/header/HeaderMenuBarTab.kt b/app/src/main/java/com/texthip/thip/ui/common/header/HeaderMenuBarTab.kt
new file mode 100644
index 00000000..0e090e39
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/common/header/HeaderMenuBarTab.kt
@@ -0,0 +1,83 @@
+package com.texthip.thip.ui.common.header
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ScrollableTabRow
+import androidx.compose.material3.Tab
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun HeaderMenuBarTab(
+ modifier: Modifier = Modifier,
+ titles: List,
+ selectedTabIndex: Int,
+ onTabSelected: (Int) -> Unit,
+ indicatorColor: Color = Color.White,
+) {
+ ScrollableTabRow(
+ selectedTabIndex = selectedTabIndex,
+ containerColor = Color.Transparent,
+ contentColor = colors.White,
+ edgePadding = 0.dp,
+ indicator = { tabPositions ->
+ val tabPosition = tabPositions[selectedTabIndex]
+ Box(
+ modifier = Modifier
+ .tabIndicatorOffset(tabPosition)
+ .padding(horizontal = 20.dp)
+ .height(2.dp)
+ .background(indicatorColor, shape = RoundedCornerShape(1.5.dp))
+ )
+ },
+ divider = {},
+ modifier = modifier
+ ) {
+ titles.forEachIndexed { index, title ->
+ val selected = selectedTabIndex == index
+ Tab(
+ modifier = Modifier
+ .padding(horizontal = 5.dp)
+ .height(40.dp),
+ selected = selected,
+ onClick = { onTabSelected(index) },
+ selectedContentColor = colors.White,
+ unselectedContentColor = colors.Grey02,
+ text = {
+ Text(
+ text = title,
+ style = typography.smalltitle_sb600_s18_h24,
+ textAlign = TextAlign.Start
+ )
+ }
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun HeaderMenuBarTabPreview() {
+ var selectedIndex = rememberSaveable { mutableIntStateOf(0) }
+
+ Box(modifier = Modifier) {
+ HeaderMenuBarTab(
+ titles = listOf("그룹 기록", "내 기록"),
+ selectedTabIndex = selectedIndex.value,
+ onTabSelected = { selectedIndex.value = it }
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/FilterHeaderSection.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/FilterHeaderSection.kt
new file mode 100644
index 00000000..a2bba8dc
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/FilterHeaderSection.kt
@@ -0,0 +1,100 @@
+package com.texthip.thip.ui.group.note.component
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.R
+import com.texthip.thip.ui.common.buttons.FilterChipButton
+import com.texthip.thip.ui.common.buttons.FormButton
+import com.texthip.thip.ui.common.buttons.OptionChipButton
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun FilterHeaderSection(
+ firstPage: String,
+ lastPage: String,
+ isTotalSelected: Boolean,
+ totalEnabled: Boolean = true,
+ onFirstPageChange: (String) -> Unit,
+ onLastPageChange: (String) -> Unit,
+ onTotalToggle: () -> Unit,
+) {
+ var isPageInputVisible by rememberSaveable { mutableStateOf(false) }
+ val isPageFiltered = firstPage.isNotBlank() || lastPage.isNotBlank()
+
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(20.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Row(
+ modifier = Modifier.align(Alignment.CenterStart),
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ FilterChipButton(
+ text = stringResource(R.string.view_by_page),
+ isSelected = isPageFiltered,
+ onClick = {
+ isPageInputVisible = !isPageInputVisible
+ },
+ onCloseClick = {
+ isPageInputVisible = true
+ }
+ )
+
+ OptionChipButton(
+ modifier = Modifier.height(36.dp),
+ text = stringResource(R.string.view_by_all),
+ isFilled = true,
+ isSelected = isTotalSelected,
+ enabled = totalEnabled, // ✅ 추가된 enabled 인자
+ textStyle = typography.menu_r400_s14_h24,
+ onClick = onTotalToggle
+ )
+ }
+
+ if (isPageInputVisible) {
+ FormButton(
+ firstPage = firstPage,
+ onFirstPageChange = onFirstPageChange,
+ lastPage = lastPage,
+ onLastPageChange = onLastPageChange,
+ onFinishClick = {
+ isPageInputVisible = false
+ }
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun FilterHeaderSectionPreview() {
+ var firstPage by rememberSaveable { mutableStateOf("") }
+ var lastPage by rememberSaveable { mutableStateOf("") }
+ var isTotalSelected by rememberSaveable { mutableStateOf(false) }
+
+ FilterHeaderSection(
+ firstPage = firstPage,
+ lastPage = lastPage,
+ isTotalSelected = isTotalSelected,
+ onFirstPageChange = { firstPage = it },
+ onLastPageChange = { lastPage = it },
+ onTotalToggle = { isTotalSelected = !isTotalSelected },
+ totalEnabled = true
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/TextCommentCard.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/TextCommentCard.kt
new file mode 100644
index 00000000..913276f9
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/TextCommentCard.kt
@@ -0,0 +1,78 @@
+package com.texthip.thip.ui.group.note.component
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.Text
+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.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.R
+import com.texthip.thip.ui.common.buttons.ActionBarButton
+import com.texthip.thip.ui.common.header.ProfileBar
+import com.texthip.thip.ui.group.note.mock.GroupNoteRecord
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun TextCommentCard(
+ data: GroupNoteRecord,
+) {
+ var isLiked by remember { mutableStateOf(data.isLiked) }
+
+ Column(
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ ProfileBar(
+// profileImage = data.profileImageUrl,
+ profileImage = painterResource(R.drawable.character_literature),
+ topText = data.nickName,
+ bottomText = data.page.toString() + stringResource(R.string.page),
+ bottomTextColor = colors.Purple,
+ showSubscriberInfo = false,
+// hoursAgo = data.postDate
+ hoursAgo = data.postDate
+ )
+
+ Text(
+ text = data.content,
+ style = typography.feedcopy_r400_s14_h20,
+ color = colors.Grey,
+ )
+
+ ActionBarButton(
+ isLiked = isLiked,
+ likeCount = data.likeCount,
+ commentCount = data.commentCount,
+ onLikeClick = {
+ isLiked = !isLiked
+ },
+ onCommentClick = { },
+ )
+ }
+}
+
+@Preview
+@Composable
+fun TextCommentCardPreview() {
+ TextCommentCard(
+ data = GroupNoteRecord(
+ page = 132,
+ postDate = 12,
+ userId = 1,
+ nickName = "user.01",
+ profileImageUrl = "https://example.com/profile.jpg",
+ content = "내 생각에 이 부분이 가장 어려운 것 같다. 비유도 난해하고 잘 이해가 가지 않는데 다른 메이트들은 어떻게 읽었나요?",
+ likeCount = 123,
+ commentCount = 123,
+ isLiked = true,
+ isWriter = false,
+ recordId = 1
+ )
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/mock/CommentData.kt b/app/src/main/java/com/texthip/thip/ui/group/note/mock/CommentData.kt
new file mode 100644
index 00000000..345cd846
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/note/mock/CommentData.kt
@@ -0,0 +1,51 @@
+package com.texthip.thip.ui.group.note.mock
+
+sealed class GroupNoteItem {
+// abstract val postDate: String
+ abstract val postDate: Int // TODO: String으로 바꾸기
+ abstract val page: Int
+ abstract val userId: Int
+ abstract val nickName: String
+ abstract val profileImageUrl: String
+ abstract val content: String
+ abstract val likeCount: Int
+ abstract val commentCount: Int
+ abstract val isLiked: Boolean
+ abstract val isWriter: Boolean
+}
+
+data class GroupNoteRecord(
+ override val postDate: Int,
+ override val page: Int,
+ override val userId: Int,
+ override val nickName: String,
+ override val profileImageUrl: String,
+ override val content: String,
+ override val likeCount: Int,
+ override val commentCount: Int,
+ override val isLiked: Boolean,
+ override val isWriter: Boolean,
+ val recordId: Int
+) : GroupNoteItem()
+
+data class GroupNoteVote(
+ override val postDate: Int,
+ override val page: Int,
+ override val userId: Int,
+ override val nickName: String,
+ override val profileImageUrl: String,
+ override val content: String,
+ override val likeCount: Int,
+ override val commentCount: Int,
+ override val isLiked: Boolean,
+ override val isWriter: Boolean,
+ val voteId: Int,
+ val voteItems: List
+) : GroupNoteItem()
+
+data class VoteItem(
+ val voteItemId: Int,
+ val itemName: String,
+ val percentage: Int,
+ val isVoted: Boolean
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt
index 86003a6e..d9456c0e 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt
@@ -10,13 +10,22 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ExperimentalMaterial3Api
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.Modifier
+import androidx.compose.ui.draw.blur
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.texthip.thip.R
+import com.texthip.thip.ui.common.bottomsheet.MenuBottomSheet
+import com.texthip.thip.ui.common.bottomsheet.MenuBottomSheetItem
import com.texthip.thip.ui.common.topappbar.GradationTopAppBar
import com.texthip.thip.ui.group.room.component.GroupRoomBody
import com.texthip.thip.ui.group.room.component.GroupRoomHeader
@@ -24,18 +33,25 @@ import com.texthip.thip.ui.group.room.mock.GroupRoomBodyData
import com.texthip.thip.ui.group.room.mock.GroupRoomHeaderData
import com.texthip.thip.ui.group.room.mock.VoteData
import com.texthip.thip.ui.theme.ThipTheme
+import com.texthip.thip.ui.theme.ThipTheme.colors
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GroupRoomScreen() {
val scrollState = rememberScrollState()
+ var isBottomSheetVisible by remember { mutableStateOf(false) }
+
Box(
- modifier = Modifier
- .fillMaxSize()
+ if (isBottomSheetVisible) {
+ Modifier
+ .fillMaxSize()
+ .blur(5.dp)
+ } else {
+ Modifier.fillMaxSize()
+ }
) {
- Box(
- modifier = Modifier.verticalScroll(scrollState)
- ) {
+ Box(modifier = Modifier.verticalScroll(scrollState)) {
Image(
painter = painterResource(R.drawable.img_group_room),
contentDescription = null,
@@ -94,9 +110,35 @@ fun GroupRoomScreen() {
)
}
}
+
GradationTopAppBar(
onLeftClick = {},
- onRightClick = {},
+ onRightClick = { isBottomSheetVisible = true },
+ )
+ }
+
+ if (isBottomSheetVisible) {
+ MenuBottomSheet(
+ items = listOf(
+ MenuBottomSheetItem(
+ text = stringResource(R.string.leave_room),
+ color = colors.White,
+ onClick = { }
+ ),
+ MenuBottomSheetItem(
+ text = stringResource(R.string.report_room),
+ color = colors.Red,
+ onClick = { }
+ ),
+ MenuBottomSheetItem(
+ text = stringResource(R.string.delete_room),
+ color = colors.Red,
+ onClick = { }
+ )
+ ),
+ onDismiss = {
+ isBottomSheetVisible = false
+ }
)
}
}
diff --git a/app/src/main/res/drawable/ic_heart.xml b/app/src/main/res/drawable/ic_heart.xml
index 09411a02..46b5002b 100644
--- a/app/src/main/res/drawable/ic_heart.xml
+++ b/app/src/main/res/drawable/ic_heart.xml
@@ -1,10 +1,10 @@
독서메이트
님에게 답글 작성
메이트들과 간단한 인사를 나눠보세요!
-
+ 방 나가기
+ 방 신고하기
+ 방 삭제하기
+ ~
+ p
+ 그룹 기록
+ 내 기록
+ 페이지별 보기
+ 총평 보기
\ No newline at end of file