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