diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..7f70f7ef
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..79ee123c
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 62464c5c..5e471df0 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,8 @@
+
-
+
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipButton.kt
index 6cd73363..772b7838 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipButton.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipButton.kt
@@ -29,17 +29,18 @@ import com.texthip.thip.ui.theme.ThipTheme.typography
fun GenreChipButton(
modifier: Modifier = Modifier,
text: String,
- onClick: () -> Unit = { }
+ onClick: () -> Unit = {},
+ onCloseClick: () -> Unit = {}
) {
Box(
modifier = modifier
.border(
width = 1.dp,
- color = colors.White,
+ color = colors.Grey02,
shape = RoundedCornerShape(20.dp)
)
.background(color = Color.Transparent, shape = RoundedCornerShape(12.dp))
- .padding(top = 4.dp, bottom = 4.dp, end = 8.dp, start = 12.dp)
+ .padding(top = 8.dp, bottom = 8.dp, end = 8.dp, start = 12.dp)
.clickable {
onClick()
},
@@ -47,11 +48,11 @@ fun GenreChipButton(
) {
Row(
verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.spacedBy(2.dp),
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = text,
- color = colors.White,
+ color = colors.Grey01,
style = typography.info_r400_s12,
)
Icon(
@@ -60,6 +61,9 @@ fun GenreChipButton(
tint = Color.Unspecified,
modifier = Modifier
.size(20.dp)
+ .clickable {
+ onCloseClick()
+ }
)
}
}
diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt
index 1a69c9f5..904abaaf 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt
@@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import com.texthip.thip.ui.theme.ThipTheme
@Composable
fun GenreChipRow(
@@ -22,7 +23,13 @@ fun GenreChipRow(
text = genre,
isFilled = true,
isSelected = selectedIndex == idx,
- onClick = { onSelect(idx) }
+ onClick = {
+ if (selectedIndex == idx) {
+ onSelect(-1)
+ } else {
+ onSelect(idx)
+ }
+ }
)
if (idx < genres.size - 1) {
Spacer(modifier = modifier)
@@ -31,12 +38,14 @@ fun GenreChipRow(
}
}
-@Preview(showBackground = true, backgroundColor = 0xFF000000, widthDp = 360)
+@Preview()
@Composable
fun PreviewGenreChipRow() {
- GenreChipRow(
- genres = listOf("문학", "과학·IT", "사회과학", "인문학", "예술"),
- selectedIndex = 0,
- onSelect = {}
- )
+ ThipTheme {
+ GenreChipRow(
+ genres = listOf("문학", "과학·IT", "사회과학", "인문학", "예술"),
+ selectedIndex = 0,
+ onSelect = {}
+ )
+ }
}
diff --git a/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt b/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt
index 5a7b8137..a58d94da 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt
@@ -41,7 +41,7 @@ fun CardItemRoom(
participants: Int,
maxParticipants: Int,
isRecruiting: Boolean,
- endDate: Int,
+ endDate: Int? = null,
imageRes: Int? = R.drawable.bookcover_sample,
hasBorder: Boolean = false,
onClick: () -> Unit = {}
@@ -91,9 +91,11 @@ fun CardItemRoom(
Spacer(modifier = Modifier.width(16.dp))
Column(
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(107.dp),
+ verticalArrangement = Arrangement.Center
) {
- Spacer(modifier = Modifier.height(16.dp))
Text(
text = title,
color = colors.White,
@@ -139,7 +141,10 @@ fun CardItemRoom(
verticalAlignment = Alignment.CenterVertically
) {
Text(
- text = stringResource(R.string.card_item_participant, participants),
+ text = stringResource(
+ R.string.card_item_participating_count,
+ participants
+ ),
style = typography.menu_sb600_s12,
color = colors.White
)
@@ -153,20 +158,22 @@ fun CardItemRoom(
}
}
}
- Spacer(modifier = Modifier.height(5.dp))
+ endDate?.let {
+ Spacer(modifier = Modifier.height(5.dp))
- Text(
- text = stringResource(
- R.string.card_item_end_date,
- endDate
- ) + if (isRecruiting) stringResource(
- R.string.card_item_end
- ) else stringResource(R.string.card_item_finish),
+ Text(
+ text = stringResource(
+ R.string.card_item_end_date,
+ endDate
+ ) + if (isRecruiting) stringResource(
+ R.string.card_item_end
+ ) else stringResource(R.string.card_item_finish),
- color = if (isRecruiting) colors.Red else colors.Grey01,
- style = typography.menu_sb600_s12_h20,
- maxLines = 1
- )
+ color = if (isRecruiting) colors.Red else colors.Grey01,
+ style = typography.menu_sb600_s12_h20,
+ maxLines = 1
+ )
+ }
}
}
}
@@ -213,6 +220,14 @@ fun CardItemRoomPreview() {
imageRes = R.drawable.bookcover_sample,
hasBorder = true
)
+ CardItemRoom(
+ title = "모임방 이름입니다. 모임방 이름입니다.",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = false,
+ imageRes = R.drawable.bookcover_sample,
+ hasBorder = true
+ )
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoomSmall.kt b/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoomSmall.kt
index 8cac5e5b..eafaf728 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoomSmall.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoomSmall.kt
@@ -29,6 +29,7 @@ import androidx.compose.ui.text.style.TextOverflow
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
import com.texthip.thip.ui.theme.ThipTheme.colors
import com.texthip.thip.ui.theme.ThipTheme.typography
@@ -38,16 +39,27 @@ fun CardItemRoomSmall(
title: String,
participants: Int,
maxParticipants: Int,
- endDate: Int,
+ endDate: Int?,
imageRes: Int? = R.drawable.bookcover_sample_small,
+ isWide: Boolean = false,
+ isSecret: Boolean = false,
onClick: () -> Unit = {}
) {
+ val cardModifier = if (isWide) {
+ modifier
+ .fillMaxWidth()
+ } else {
+ modifier
+ .width(232.dp)
+ }
+ val bgColor = if (isWide) colors.Black else colors.DarkGrey50
+
Card(
- modifier = modifier
- .size(width = 232.dp, height = 104.dp)
+ modifier = cardModifier
+ .height(104.dp)
.clickable { onClick() },
colors = CardDefaults.cardColors(
- containerColor = colors.DarkGrey50
+ containerColor = bgColor
),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
shape = RoundedCornerShape(12.dp)
@@ -73,9 +85,17 @@ fun CardItemRoomSmall(
contentScale = ContentScale.Crop
)
}
+ if (isSecret) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_secret_cover),
+ contentDescription = "비밀방",
+ modifier = Modifier
+ .fillMaxSize()
+ )
+ }
}
- Spacer(modifier = Modifier.width(8.dp))
+ Spacer(modifier = Modifier.width(12.dp))
Column(
modifier = Modifier.fillMaxWidth()
@@ -84,7 +104,7 @@ fun CardItemRoomSmall(
Text(
text = title,
color = colors.White,
- style = typography.menu_sb600_s14_h24,
+ style = if (isWide) typography.smalltitle_sb600_s18_h24 else typography.menu_sb600_s14_h24,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -123,29 +143,42 @@ fun CardItemRoomSmall(
}
}
Spacer(modifier = Modifier.height(4.dp))
- Text(
- text = stringResource(R.string.card_item_end_date_recruit, endDate),
- color = colors.Red,
- style = typography.menu_sb600_s12_h20
- )
+
+ endDate?.let {
+ Text(
+ text = stringResource(R.string.card_item_end_date_recruit, endDate),
+ color = colors.Red,
+ style = typography.menu_sb600_s12_h20
+ )
+ }
}
}
}
}
}
-
-@Preview(showBackground = true, backgroundColor = 0xFF000000, widthDp = 360)
+@Preview()
@Composable
fun CardItemRoomSmallPreview() {
- Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
- CardItemRoomSmall(
- title = "방 제목입니다 방 제목입니다",
- participants = 22,
- maxParticipants = 30,
- endDate = 3,
- imageRes = R.drawable.bookcover_sample
- )
+ ThipTheme {
+ Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
+ CardItemRoomSmall(
+ title = "방 제목입니다 방 제목입니다",
+ participants = 22,
+ maxParticipants = 30,
+ endDate = 3,
+ imageRes = R.drawable.bookcover_sample
+ )
+
+ CardItemRoomSmall(
+ title = "와이드 카드 fillMaxWidth",
+ participants = 18,
+ maxParticipants = 25,
+ endDate = 5,
+ imageRes = R.drawable.bookcover_sample,
+ isWide = true
+ )
+ }
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/texthip/thip/ui/common/forms/SearchBookTextField.kt b/app/src/main/java/com/texthip/thip/ui/common/forms/SearchBookTextField.kt
index f841086e..cb253672 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/forms/SearchBookTextField.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/forms/SearchBookTextField.kt
@@ -1,7 +1,7 @@
package com.texthip.thip.ui.common.forms
+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.Row
import androidx.compose.foundation.layout.Spacer
@@ -9,13 +9,13 @@ 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.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Icon
-import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
-import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -23,83 +23,105 @@ 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.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import com.texthip.thip.R
+import com.texthip.thip.ui.theme.ThipTheme
import com.texthip.thip.ui.theme.ThipTheme.colors
import com.texthip.thip.ui.theme.ThipTheme.typography
@Composable
fun SearchBookTextField(
modifier: Modifier = Modifier,
+ text: String,
hint: String,
+ onValueChange: (String) -> Unit,
onSearch: (String) -> Unit = {}
) {
- var text by rememberSaveable { mutableStateOf("") }
- val myStyle = typography.menu_r400_s14_h24.copy(lineHeight = 14.sp)
+ val myStyle = typography.menu_r400_s14_h24.copy(
+ color = colors.White
+ )
+ val shape = RoundedCornerShape(12.dp)
Box(
- modifier = modifier.height(48.dp)
+ modifier = modifier
+ .fillMaxWidth()
+ .height(40.dp)
+ .clip(shape)
+ .background(colors.DarkGrey02),
+ contentAlignment = Alignment.CenterStart
) {
- OutlinedTextField(
- value = text,
- onValueChange = {
- text = it
- },
- placeholder = {
- Text(
- text = hint,
- color = colors.Grey02,
- style = myStyle
- )
- },
- textStyle = myStyle,
- modifier = Modifier.fillMaxSize(),
- shape = RoundedCornerShape(12.dp),
- colors = TextFieldDefaults.colors(
- focusedTextColor = colors.White,
- focusedIndicatorColor = Color.Transparent,
- unfocusedIndicatorColor = Color.Transparent,
- focusedContainerColor = colors.DarkGrey02,
- unfocusedContainerColor = colors.DarkGrey02,
- cursorColor = colors.NeonGreen
- ),
- trailingIcon = {
- Row(
- horizontalArrangement = Arrangement.Center
- ) {
- Icon(
- painter = painterResource(id = R.drawable.ic_x_circle_grey),
- contentDescription = "Clear text",
- modifier = Modifier
- .clickable { text = "" },
- tint = Color.Unspecified
- )
-
- Spacer(Modifier.width(20.dp))
- Icon(
- painter = painterResource(id = R.drawable.ic_search),
- contentDescription = "Search",
- modifier = Modifier
- .clickable { onSearch(text) },
- tint = colors.White
- )
- Spacer(Modifier.width(8.dp))
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ BasicTextField(
+ value = text,
+ onValueChange = { onValueChange(it) },
+ singleLine = true,
+ textStyle = myStyle,
+ cursorBrush = SolidColor(colors.NeonGreen),
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
+ keyboardActions = KeyboardActions(
+ onSearch = { onSearch(text) }
+ ),
+ modifier = Modifier
+ .weight(1f)
+ .padding(start = 12.dp, end = 8.dp),
+ decorationBox = { innerTextField ->
+ Box(contentAlignment = Alignment.CenterStart) {
+ if (text.isEmpty()) {
+ Text(
+ text = hint,
+ color = colors.Grey02,
+ style = myStyle
+ )
+ }
+ innerTextField()
+ }
}
- },
- singleLine = true
- )
+ )
+
+ if (text.isNotEmpty()) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_x_circle_grey),
+ contentDescription = "Clear text",
+ modifier = Modifier
+ .clickable { onValueChange("") },
+ tint = Color.Unspecified
+ )
+ }
+ Spacer(Modifier.width(20.dp))
+ Icon(
+ painter = painterResource(id = R.drawable.ic_search),
+ contentDescription = "Search",
+ modifier = Modifier
+ .clickable {
+ onSearch(text)
+ },
+ tint = colors.White
+ )
+ Spacer(Modifier.width(12.dp))
+ }
}
}
+
@Preview()
@Composable
private fun SearchBookTextFieldPreview() {
- SearchBookTextField(
- hint = "책 제목, 저자검색",
- onSearch = { /* 검색 실행 */ }
- )
+ ThipTheme {
+ var text by rememberSaveable { mutableStateOf("") }
+ SearchBookTextField(
+ text = text,
+ hint = "책 제목, 저자검색",
+ onValueChange = { text = it },
+ onSearch = { /* 검색 실행 */ }
+ )
+ }
}
diff --git a/app/src/main/java/com/texthip/thip/ui/common/forms/SingleDigitTextBox.kt b/app/src/main/java/com/texthip/thip/ui/common/forms/SingleDigitTextBox.kt
new file mode 100644
index 00000000..380fb8a6
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/common/forms/SingleDigitTextBox.kt
@@ -0,0 +1,107 @@
+package com.texthip.thip.ui.common.forms
+
+import android.view.KeyEvent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+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.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.type
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.texthip.thip.ui.theme.ThipTheme
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun SingleDigitBox(
+ modifier: Modifier = Modifier,
+ value: String,
+ onValueChange: (String) -> Unit,
+ onBackspace: (() -> Unit)? = null,
+ containerColor: Color = colors.darkGray01,
+ borderColor: Color = Color.Transparent
+) {
+ val myStyle = typography.smalltitle_sb600_s18_h24.copy(
+ lineHeight = 20.sp,
+ textAlign = TextAlign.Center,
+ color = colors.White
+ )
+
+ Box(
+ modifier = modifier
+ .size(44.dp)
+ .background(containerColor, RoundedCornerShape(12.dp))
+ .border(1.dp, borderColor, RoundedCornerShape(12.dp)),
+ contentAlignment = Alignment.Center
+ ) {
+ BasicTextField(
+ value = value,
+ onValueChange = { input ->
+ val filtered = input.filter { it.isDigit() }.take(1)
+ onValueChange(filtered)
+ },
+ textStyle = myStyle,
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
+ modifier = modifier
+ .background(containerColor, RoundedCornerShape(12.dp))
+ .border(1.dp, borderColor, RoundedCornerShape(12.dp))
+ .onKeyEvent { keyEvent ->
+ if (keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_DEL &&
+ keyEvent.type == KeyEventType.KeyDown
+ ) {
+ if (value.isEmpty()) {
+ onBackspace?.invoke()
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ },
+ cursorBrush = SolidColor(colors.NeonGreen),
+ decorationBox = { innerTextField ->
+ Box(
+ Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) { innerTextField() }
+ }
+ )
+ }
+}
+
+@Preview
+@Composable
+fun PreviewSingleDigitBox() {
+ ThipTheme {
+ var digit by rememberSaveable { mutableStateOf("") }
+ Box(
+ modifier = Modifier.size(60.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ SingleDigitBox(
+ value = digit,
+ onValueChange = { digit = it }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/common/forms/WarningTextField.kt b/app/src/main/java/com/texthip/thip/ui/common/forms/WarningTextField.kt
index cdeac4d1..02e0a799 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/forms/WarningTextField.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/forms/WarningTextField.kt
@@ -5,7 +5,6 @@ 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.padding
import androidx.compose.foundation.layout.size
@@ -39,7 +38,7 @@ fun WarningTextField(
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
hint: String,
- warningMessage: String = "경고 메시지를 입력해주세요.",
+ warningMessage: String = "",
showWarning: Boolean = false,
showLimit: Boolean = true,
maxLength: Int = Int.MAX_VALUE,
diff --git a/app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt b/app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt
index 23a5fdb7..1d8d1687 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt
@@ -32,7 +32,7 @@ fun ToastWithDate(
.clip(RoundedCornerShape(12.dp))
.background(color = colors.DarkGrey02)
.border(
- width = 2.dp,
+ width = 1.dp,
color = colors.Grey02,
shape = RoundedCornerShape(12.dp)
)
diff --git a/app/src/main/java/com/texthip/thip/ui/common/topappbar/DefaultTopAppBar.kt b/app/src/main/java/com/texthip/thip/ui/common/topappbar/DefaultTopAppBar.kt
index 0baeae07..9c762d0e 100644
--- a/app/src/main/java/com/texthip/thip/ui/common/topappbar/DefaultTopAppBar.kt
+++ b/app/src/main/java/com/texthip/thip/ui/common/topappbar/DefaultTopAppBar.kt
@@ -31,7 +31,6 @@ fun DefaultTopAppBar(
Box(
modifier = Modifier
.fillMaxWidth()
- .background(color = colors.Black)
.padding(horizontal = 20.dp, vertical = 16.dp)
) {
Icon(
diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt
index 3c31caf8..68682685 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt
@@ -40,6 +40,7 @@ fun GroupBookSearchBottomSheet(
stringResource(R.string.group_saved_book), stringResource(R.string.group_book)
)
val books = if (selectedTab == 0) savedBooks else groupBooks
+ var searchText by rememberSaveable { mutableStateOf("") }
CustomBottomSheet(
onDismiss = onDismiss
@@ -52,7 +53,9 @@ fun GroupBookSearchBottomSheet(
// 검색창
SearchBookTextField(
hint = stringResource(R.string.group_book_search_hint),
- onSearch = { /* 검색 구현 */ }
+ text = searchText,
+ onValueChange = { searchText = it },
+ onSearch = { /* 검색 구현 */ },
)
Spacer(Modifier.height(20.dp))
}
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt
index a6fb52fd..9526b611 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt
@@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.texthip.thip.R
@@ -49,8 +50,8 @@ fun GroupRoomDeadlineSection(
) {
val horizontalPadding = sideMargin
val cardWidth = maxWidth - (horizontalPadding * 2)
- val scale = 0.9f
- val desiredGap = 12.dp // TODO: 이 부분을 10dp로 하면 양 옆의 카드에 살짝 다음 내용이 보여서 12정도가 어떤지
+ val scale = 0.94f
+ val desiredGap = 12.dp // TODO: 이 부분을 10dp로 하면 양 옆의 카드에 살짝 다음 내용이 보여서 12정도가 어떤지
val pageSpacing = (-(cardWidth - (cardWidth * scale)) / 2) + desiredGap
@@ -64,7 +65,7 @@ fun GroupRoomDeadlineSection(
var selectedGenre by remember { mutableIntStateOf(0) }
val isCurrent = pagerState.currentPage == page
- val scale = if (isCurrent) 1f else 0.9f
+ val scale = if (isCurrent) 1f else 0.94f
Box(
modifier = Modifier
@@ -102,24 +103,54 @@ fun GroupRoomDeadlineSection(
)
Spacer(Modifier.height(20.dp))
- val cards = section.rooms.filter { it.genreIndex == selectedGenre }.take(3)
+ val cards = section.rooms.filter { it.genreIndex == selectedGenre }
Column(
verticalArrangement = Arrangement.spacedBy(20.dp),
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(584.dp)
) {
- cards.forEach { room ->
- CardItemRoom(
- title = room.title,
- participants = room.participants,
- maxParticipants = room.maxParticipants,
- isRecruiting = room.isRecruiting,
- endDate = room.endDate,
- imageRes = room.imageRes,
- onClick = { onRoomClick(room) },
- hasBorder = true,
- )
+ if (cards.isEmpty()) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Spacer(Modifier.height(40.dp))
+ Text(
+ text = stringResource(R.string.group_no_room_exist),
+ style = typography.smalltitle_sb600_s16_h20,
+ color = colors.White,
+ textAlign = TextAlign.Center
+ )
+ Spacer(Modifier.height(8.dp))
+ Text(
+ text = stringResource(R.string.group_no_room_error_comment),
+ style = typography.copy_r400_s14,
+ color = colors.Grey,
+ textAlign = TextAlign.Center
+ )
+ }
+ } else {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ cards.forEach { room ->
+ CardItemRoom(
+ title = room.title,
+ participants = room.participants,
+ maxParticipants = room.maxParticipants,
+ isRecruiting = room.isRecruiting,
+ endDate = room.endDate,
+ imageRes = room.imageRes,
+ onClick = { onRoomClick(room) },
+ hasBorder = true,
+ )
+ }
+ }
}
- if (cards.size < 3) {
+
+ if (cards.size < 4) {
Spacer(
modifier = Modifier
.weight(1f, fill = true)
@@ -166,6 +197,14 @@ fun PreviewGroupRoomPagerSection() {
endDate = 3,
genreIndex = 0
),
+ GroupCardItemRoomData(
+ title = "명작 같이 읽기방",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = true,
+ endDate = 3,
+ genreIndex = 0
+ ),
GroupCardItemRoomData(
title = "물리책 읽는 방",
participants = 13,
@@ -250,6 +289,39 @@ fun PreviewGroupRoomPagerSection() {
)
)
+ GroupRoomDeadlineSection(
+ roomSections = roomSections,
+ onRoomClick = {}
+ )
+ }
+}
+
+@Preview(name = "Empty Genre Data")
+@Composable
+fun PreviewGroupRoomPagerSectionEmptyGenre() {
+ ThipTheme {
+ val genres = listOf("문학", "과학·IT", "사회과학", "인문학", "예술")
+
+ // 특정 장르에만 데이터가 있는 경우 (문학 장르만 데이터 존재)
+ val deadlineRooms = listOf(
+ GroupCardItemRoomData(
+ title = "시집만 읽는 사람들 3월",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = true,
+ endDate = 3,
+ genreIndex = 0 // 문학 장르만
+ )
+ )
+
+ val roomSections = listOf(
+ GroupRoomSectionData(
+ title = "마감 임박한 독서 모임방",
+ rooms = deadlineRooms,
+ genres = genres
+ )
+ )
+
GroupRoomDeadlineSection(
roomSections = roomSections,
onRoomClick = {}
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupEmptyCard.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupEmptyCard.kt
new file mode 100644
index 00000000..1b11017b
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupEmptyCard.kt
@@ -0,0 +1,106 @@
+package com.texthip.thip.ui.group.myroom.component
+
+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.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.shape.RoundedCornerShape
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+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.theme.ThipTheme
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun GroupEmptyCard(
+ modifier: Modifier = Modifier,
+ backgroundColor: Color = Color.White,
+ onClick: () -> Unit = {}
+) {
+ // 그라데이션
+ val gradient = Brush.linearGradient(
+ colors = listOf(
+ colors.White,
+ colors.Grey01
+ ),
+ start = Offset(0f, 0f),
+ end = Offset(1000f, 1000f)
+ )
+
+ Card(
+ modifier = modifier
+ .fillMaxWidth()
+ .height(176.dp)
+ .clickable { onClick() },
+ shape = RoundedCornerShape(18.dp),
+ colors = CardDefaults.cardColors(containerColor = backgroundColor),
+ elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
+ ) {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .background(brush = gradient)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(top = 34.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.SpaceBetween // 위, 아래 끝에 배치
+ ) {
+ Column (
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = stringResource(R.string.group_empty_card_title),
+ style = typography.smalltitle_sb600_s18_h24,
+ color = colors.Black
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+
+ Text(
+ text = stringResource(R.string.group_empty_card_description),
+ style = typography.copy_r400_s14,
+ color = colors.Grey02
+ )
+ }
+
+ Icon(
+ painter = painterResource(id = R.drawable.ic_thip_empty_room),
+ contentDescription = "Empty Group Icon",
+ tint = Color.Unspecified
+ )
+ }
+ }
+ }
+}
+
+
+@Preview()
+@Composable
+fun GroupEmptyCardPreview() {
+ ThipTheme {
+ GroupEmptyCard(
+ onClick = {}
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupPager.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupPager.kt
index 3b2ab2a2..4ee9a057 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupPager.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupPager.kt
@@ -1,10 +1,16 @@
package com.texthip.thip.ui.group.myroom.component
import android.annotation.SuppressLint
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
@@ -27,7 +33,6 @@ fun GroupPager(
BoxWithConstraints(
modifier = Modifier
.fillMaxWidth()
- .height(192.dp)
) {
val horizontalPadding = 30.dp
val cardWidth = maxWidth - (horizontalPadding * 2)
@@ -36,48 +41,90 @@ fun GroupPager(
(-(cardWidth - (cardWidth * scale)) / 2f) + desiredGap
}
- val pagerState = rememberPagerState(
- initialPage = 0,
- pageCount = { maxOf(1, groupCards.size) }
- )
-
- HorizontalPager(
- state = pagerState,
- contentPadding = PaddingValues(start = horizontalPadding, end = horizontalPadding),
- pageSpacing = pageSpacing,
- modifier = Modifier.fillMaxWidth()
- ) { page ->
- val isCurrent = pagerState.currentPage == page
- val scale = if (isCurrent) 1f else 0.86f
- val bgColor = if (isCurrent) colors.White else colors.DarkGrey
-
+ // 데이터가 없는 경우 Empty State 표시
+ if (groupCards.isEmpty()) {
+ GroupEmptyCard(
+ modifier = Modifier.padding(horizontal = horizontalPadding)
+ )
+ } else if (groupCards.size == 1) {
+ // 데이터가 하나일 때는 스크롤 없이 고정 카드 표시
Box(
modifier = Modifier
- .width(cardWidth)
- .graphicsLayer {
- scaleX = scale
- scaleY = scale
- alpha = if (isCurrent) 1f else 0.7f
- }
+ .fillMaxWidth()
+ .padding(horizontal = horizontalPadding),
+ contentAlignment = Alignment.Center
) {
GroupMainCard(
- data = groupCards[page],
- onClick = { onCardClick(groupCards[page]) },
- backgroundColor = bgColor
+ data = groupCards[0],
+ onClick = { onCardClick(groupCards[0]) },
+ backgroundColor = colors.White
)
}
- }
+ } else {
+ // 여러 데이터일 때는 무한 스크롤 적용
+ val infinitePageCount = Int.MAX_VALUE
+ val startPage = infinitePageCount / 2
- SimplePagerIndicator(
- pageCount = groupCards.size,
- currentPage = pagerState.currentPage,
- modifier = Modifier
- .align(Alignment.BottomCenter)
- .padding(top = 12.dp)
- )
+ val pagerState = rememberPagerState(
+ initialPage = startPage,
+ pageCount = { infinitePageCount }
+ )
+
+ // 시작 페이지로 이동
+ LaunchedEffect(groupCards.size) {
+ pagerState.scrollToPage(startPage)
+ }
+
+ HorizontalPager(
+ state = pagerState,
+ contentPadding = PaddingValues(start = horizontalPadding, end = horizontalPadding),
+ pageSpacing = pageSpacing,
+ modifier = Modifier.fillMaxWidth()
+ ) { page ->
+ val actualIndex = ((page - startPage) % groupCards.size + groupCards.size) % groupCards.size
+ val currentPageIndex = ((pagerState.currentPage - startPage) % groupCards.size + groupCards.size) % groupCards.size
+
+ val isCurrent = actualIndex == currentPageIndex
+ val scale = if (isCurrent) 1f else 0.86f
+
+ // 현재 카드와 양옆 카드의 색상 설정
+ val bgColor = when {
+ isCurrent -> colors.White
+ else -> colors.DarkGrey
+ }
+
+ Box(
+ modifier = Modifier
+ .width(cardWidth)
+ .graphicsLayer {
+ scaleX = scale
+ scaleY = scale
+ alpha = if (isCurrent) 1f else 0.7f
+ }
+ ) {
+ GroupMainCard(
+ data = groupCards[actualIndex],
+ onClick = { onCardClick(groupCards[actualIndex]) },
+ backgroundColor = bgColor
+ )
+ }
+ }
+
+ // 페이지 인디케이터 였던 것 (혹시 몰라 남겨둡니다)
+ /*val currentPageIndex = ((pagerState.currentPage - startPage) % groupCards.size + groupCards.size) % groupCards.size
+
+ SimplePagerIndicator(
+ pageCount = groupCards.size,
+ currentPage = currentPageIndex,
+ modifier = Modifier
+ .align(Alignment.BottomCenter)
+ .padding(top = 12.dp)
+ )*/
+ }
}
}
+
@Preview()
@Composable
fun PreviewMyGroupPager() {
@@ -108,3 +155,28 @@ fun PreviewMyGroupPager() {
GroupPager(groupCards = list, onCardClick = {})
}
}
+
+@Preview()
+@Composable
+fun PreviewSingleGroupPager() {
+ ThipTheme {
+ val singleList = listOf(
+ GroupCardData(
+ title = "단일 그룹",
+ members = 15,
+ imageRes = R.drawable.bookcover_sample,
+ progress = 60,
+ nickname = "single님"
+ )
+ )
+ GroupPager(groupCards = singleList, onCardClick = {})
+ }
+}
+
+@Preview()
+@Composable
+fun PreviewEmptyGroupPager() {
+ ThipTheme {
+ GroupPager(groupCards = emptyList(), onCardClick = {})
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupSearchTextField.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupSearchTextField.kt
index 073a08c8..0aa99d1a 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupSearchTextField.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupSearchTextField.kt
@@ -1,22 +1,16 @@
package com.texthip.thip.ui.group.myroom.component
-import androidx.compose.foundation.layout.Box
-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.background
+import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Icon
-import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
-import androidx.compose.material3.TextFieldDefaults
-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.runtime.*
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -28,58 +22,76 @@ import com.texthip.thip.ui.theme.ThipTheme.typography
@Composable
fun GroupSearchTextField(
- onValueChange: (String) -> Unit
+ modifier: Modifier = Modifier,
+ value: String,
+ placeholder: String = stringResource(R.string.group_search_placeholder),
+ onValueChange: (String) -> Unit,
+ onClick: (() -> Unit)? = null
) {
- var value by rememberSaveable { mutableStateOf("") }
- val textStyle = typography.menu_r400_s14_h24.copy(lineHeight = 20.sp)
+ val textStyle = typography.menu_r400_s14_h24.copy(
+ fontSize = 14.sp,
+ lineHeight = 16.sp,
+ color = colors.White
+ )
+ val shape = RoundedCornerShape(12.dp)
+ val backgroundColor = colors.DarkGrey50
+ val cursorColor = colors.NeonGreen
Box(
- Modifier
+ modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
- .height(48.dp)
+ .height(40.dp)
+ .clip(shape)
+ .background(backgroundColor),
+ contentAlignment = Alignment.CenterStart
) {
- OutlinedTextField(
- value = value,
- onValueChange = { newValue ->
- value = newValue
- onValueChange(newValue)
- },
- textStyle = textStyle,
- modifier = Modifier
- .fillMaxWidth(),
- placeholder = {
- Text(
- text = stringResource(R.string.group_search_placeholder),
- color = colors.Grey02,
- style = typography.menu_r400_s14_h24.copy(lineHeight = 2.sp)
- )
- },
- singleLine = true,
- shape = RoundedCornerShape(12.dp),
- colors = TextFieldDefaults.colors(
- focusedTextColor = colors.White,
- focusedIndicatorColor = Color.Transparent,
- unfocusedIndicatorColor = Color.Transparent,
- focusedContainerColor = colors.DarkGrey,
- unfocusedContainerColor = colors.DarkGrey,
- cursorColor = colors.NeonGreen
- ),
- trailingIcon = {
- Icon(
- painter = painterResource(id = R.drawable.ic_search),
- contentDescription = "검색",
- tint = colors.White,
- modifier = Modifier.size(24.dp)
- )
- }
- )
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ BasicTextField(
+ value = value,
+ onValueChange = onValueChange,
+ singleLine = true,
+ textStyle = textStyle,
+ cursorBrush = SolidColor(cursorColor),
+ modifier = Modifier
+ .weight(1f)
+ .padding(start = 12.dp, end = 8.dp),
+ decorationBox = { innerTextField ->
+ Box(contentAlignment = Alignment.CenterStart) {
+ if (value.isEmpty()) {
+ Text(
+ text = placeholder,
+ color = colors.Grey02,
+ style = typography.menu_r400_s14_h24.copy(
+ fontSize = 14.sp,
+ lineHeight = 16.sp
+ )
+ )
+ }
+ innerTextField()
+ }
+ }
+ )
+ Icon(
+ painter = painterResource(id = R.drawable.ic_search),
+ contentDescription = "검색",
+ tint = colors.White,
+ modifier = Modifier
+ .padding(end = 12.dp)
+ )
+ }
}
}
@Preview(showBackground = true, backgroundColor = 0xFF000000, widthDp = 360)
@Composable
-fun PreviewSearchTextField() {
- GroupSearchTextField(onValueChange = {})
+fun PreviewGroupSearchTextField() {
+ var value by remember { mutableStateOf("") }
+ GroupSearchTextField(
+ value = value,
+ onValueChange = { value = it }
+ )
}
-
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/mock/GroupCardItemRoomData.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/mock/GroupCardItemRoomData.kt
index b3fcedc2..11f779c9 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/myroom/mock/GroupCardItemRoomData.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/mock/GroupCardItemRoomData.kt
@@ -7,9 +7,10 @@ data class GroupCardItemRoomData(
val participants: Int,
val maxParticipants: Int,
val isRecruiting: Boolean,
- val endDate: Int, // 남은 일 수
+ val endDate: Int? = null, // 남은 일 수
val imageRes: Int? = R.drawable.bookcover_sample,
- val genreIndex: Int // 장르 인덱스
+ val genreIndex: Int, // 장르 인덱스
+ val isSecret: Boolean = false
)
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupEmptyResultScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupEmptyResultScreen.kt
new file mode 100644
index 00000000..0cf28824
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupEmptyResultScreen.kt
@@ -0,0 +1,39 @@
+package com.texthip.thip.ui.group.myroom.screen
+
+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.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.ui.theme.ThipTheme
+
+@Composable
+fun GroupEmptyResultScreen(
+ mainText: String,
+ subText: String
+) {
+ val colors = ThipTheme.colors
+ val typography = ThipTheme.typography
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = mainText,
+ modifier = Modifier.padding(top = 20.dp),
+ color = colors.White,
+ style = typography.smalltitle_sb600_s18_h24
+ )
+ Text(
+ text = subText,
+ modifier = Modifier.padding(top = 8.dp),
+ color = colors.Grey,
+ style = typography.copy_r400_s14
+ )
+ }
+}
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupFilteredSearchResultScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupFilteredSearchResultScreen.kt
new file mode 100644
index 00000000..f18ac4c3
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupFilteredSearchResultScreen.kt
@@ -0,0 +1,88 @@
+package com.texthip.thip.ui.group.myroom.screen
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+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.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.R
+import com.texthip.thip.ui.common.buttons.GenreChipRow
+import com.texthip.thip.ui.common.cards.CardItemRoomSmall
+import com.texthip.thip.ui.group.myroom.mock.GroupCardItemRoomData
+import com.texthip.thip.ui.theme.ThipTheme
+
+@Composable
+fun GroupFilteredSearchResultScreen(
+ genres: List,
+ selectedGenreIndex: Int,
+ onGenreSelect: (Int) -> Unit,
+ resultCount: Int,
+ roomList: List
+) {
+ val colors = ThipTheme.colors
+ val typography = ThipTheme.typography
+ GenreChipRow(
+ modifier = Modifier.width(20.dp),
+ genres = genres,
+ selectedIndex = selectedGenreIndex,
+ onSelect = onGenreSelect
+ )
+ Spacer(modifier = Modifier.height(20.dp))
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(R.string.group_searched_room_size, resultCount),
+ color = colors.Grey,
+ style = typography.menu_m500_s14_h24
+ )
+ }
+ Spacer(
+ modifier = Modifier
+ .padding(top = 4.dp, bottom = 8.dp)
+ .fillMaxWidth()
+ .height(1.dp)
+ .background(colors.DarkGrey02)
+ )
+ if (roomList.isEmpty()) {
+ GroupEmptyResultScreen(
+ mainText = stringResource(R.string.group_no_search_result1),
+ subText = stringResource(R.string.group_no_search_result2)
+ )
+ } else {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ items(roomList) { room ->
+ CardItemRoomSmall(
+ title = room.title,
+ participants = room.participants,
+ maxParticipants = room.maxParticipants,
+ endDate = room.endDate,
+ imageRes = room.imageRes,
+ isWide = true,
+ isSecret = room.isSecret
+ )
+ Spacer(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(1.dp)
+ .background(colors.DarkGrey02)
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupLiveSearchResultScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupLiveSearchResultScreen.kt
new file mode 100644
index 00000000..abf2ea50
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupLiveSearchResultScreen.kt
@@ -0,0 +1,42 @@
+package com.texthip.thip.ui.group.myroom.screen
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.ui.common.cards.CardItemRoomSmall
+import com.texthip.thip.ui.group.myroom.mock.GroupCardItemRoomData
+import com.texthip.thip.ui.theme.ThipTheme.colors
+
+@Composable
+fun GroupLiveSearchResultScreen(
+ roomList: List
+) {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ items(roomList) { room ->
+ CardItemRoomSmall(
+ title = room.title,
+ participants = room.participants,
+ maxParticipants = room.maxParticipants,
+ endDate = room.endDate,
+ imageRes = room.imageRes,
+ isWide = true,
+ isSecret = room.isSecret
+ )
+ Spacer(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(1.dp)
+ .background(colors.DarkGrey02)
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt
index 7f9b4889..589f9198 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt
@@ -22,10 +22,15 @@ import androidx.compose.ui.Modifier
import com.texthip.thip.R
import com.texthip.thip.ui.theme.ThipTheme.colors
import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.res.painterResource
import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar
import com.texthip.thip.ui.group.myroom.mock.GroupCardItemRoomData
import com.texthip.thip.ui.group.myroom.component.GroupMyRoomFilterRow
import com.texthip.thip.ui.theme.ThipTheme
+import com.texthip.thip.ui.theme.ThipTheme.typography
@Composable
fun GroupMyScreen(
@@ -72,21 +77,48 @@ fun GroupMyScreen(
Spacer(modifier = Modifier.height(10.dp))
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(20.dp),
- contentPadding = PaddingValues(top = 10.dp, bottom = 20.dp),
- modifier = Modifier
- .fillMaxSize()
- ) {
- items(filteredList) { item ->
- CardItemRoom(
- title = item.title,
- participants = item.participants,
- maxParticipants = item.maxParticipants,
- isRecruiting = item.isRecruiting,
- endDate = item.endDate,
- imageRes = item.imageRes,
- onClick = { onCardClick(item) }
+ if (filteredList.isNotEmpty()) {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ contentPadding = PaddingValues(top = 10.dp, bottom = 20.dp),
+ modifier = Modifier.fillMaxSize()
+ ) {
+ items(filteredList) { item ->
+ CardItemRoom(
+ title = item.title,
+ participants = item.participants,
+ maxParticipants = item.maxParticipants,
+ isRecruiting = item.isRecruiting,
+ endDate = item.endDate,
+ imageRes = item.imageRes,
+ onClick = { onCardClick(item) }
+ )
+ }
+ }
+ } else {
+ Column(
+ modifier = Modifier
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.ic_notification),
+ contentDescription = null,
+ tint = colors.Grey02,
+ modifier = Modifier.padding(bottom = 12.dp)
+ )
+ Text(
+ text = stringResource(R.string.group_myroom_error_comment1),
+ color = colors.White,
+ style = typography.smalltitle_sb600_s18_h24
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Text(
+ text = stringResource(R.string.group_myroom_error_comment2),
+ color = colors.Grey,
+ style = typography.copy_r400_s14
)
}
}
@@ -199,4 +231,12 @@ fun MyGroupListFilterScreenPreview() {
)
GroupMyScreen(allDataList = dataList)
}
-}
\ No newline at end of file
+}
+
+@Preview()
+@Composable
+fun MyGroupListEmptyScreenPreview() {
+ ThipTheme {
+ GroupMyScreen(allDataList = emptyList())
+ }
+}
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRecentSearchScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRecentSearchScreen.kt
new file mode 100644
index 00000000..1958776f
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRecentSearchScreen.kt
@@ -0,0 +1,51 @@
+package com.texthip.thip.ui.group.myroom.screen
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.texthip.thip.R
+import com.texthip.thip.ui.common.buttons.GenreChipButton
+import com.texthip.thip.ui.theme.ThipTheme
+import androidx.compose.foundation.layout.FlowRow
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+
+@Composable
+fun GroupRecentSearchScreen(
+ recentSearches: List,
+ onSearchClick: (String) -> Unit,
+ onRemove: (String) -> Unit
+) {
+ val colors = ThipTheme.colors
+ val typography = ThipTheme.typography
+ Text(
+ text = stringResource(R.string.group_recent_search),
+ color = colors.White,
+ style = typography.menu_r400_s14_h24
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ if (recentSearches.isEmpty()) {
+ Text(
+ text = stringResource(R.string.group_no_recent_search),
+ color = colors.Grey01,
+ style = typography.menu_r400_s14_h24
+ )
+ } else {
+ FlowRow(
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ maxLines = 2,
+ ) {
+ recentSearches.take(9).forEach { keyword ->
+ GenreChipButton(
+ text = keyword,
+ onClick = { onSearchClick(keyword) },
+ onCloseClick = { onRemove(keyword) }
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomScreen.kt
index 8fa6adf6..269577eb 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomScreen.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomScreen.kt
@@ -1,5 +1,6 @@
package com.texthip.thip.ui.group.myroom.screen
+import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -19,271 +20,360 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+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.Brush
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
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 androidx.compose.ui.zIndex
import com.texthip.thip.R
import com.texthip.thip.ui.common.cards.CardItemRoomSmall
import com.texthip.thip.ui.common.cards.CardRoomBook
+import com.texthip.thip.ui.common.modal.DialogPopup
+import com.texthip.thip.ui.common.modal.ToastWithDate
import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar
import com.texthip.thip.ui.group.myroom.mock.GroupBookData
import com.texthip.thip.ui.group.myroom.mock.GroupBottomButtonType
-import com.texthip.thip.ui.group.myroom.mock.GroupRoomData
import com.texthip.thip.ui.group.myroom.mock.GroupCardItemRoomData
+import com.texthip.thip.ui.group.myroom.mock.GroupRoomData
import com.texthip.thip.ui.theme.ThipTheme
import com.texthip.thip.ui.theme.ThipTheme.colors
import com.texthip.thip.ui.theme.ThipTheme.typography
+import kotlinx.coroutines.delay
@Composable
fun GroupRoomScreen(
detail: GroupRoomData,
buttonType: GroupBottomButtonType,
- onBottomButtonClick: () -> Unit = {},
- onRecommendationClick: (GroupCardItemRoomData) -> Unit = {}
+ onRecommendationClick: (GroupCardItemRoomData) -> Unit = {},
+ onParticipation: () -> Unit = {}, // 참여
+ onCancelParticipation: () -> Unit = {}, // 참여 취소
+ onCloseRecruitment: () -> Unit = {}, // 모집 마감
+ onBackClick: () -> Unit = {} // 뒤로가기 추가
) {
- Box(Modifier.fillMaxSize()) {
- Column {
- DefaultTopAppBar(
- isRightIconVisible = false,
- isTitleVisible = false,
- onLeftClick = {},
- )
+ val context = LocalContext.current
+ var currentButtonType by remember { mutableStateOf(buttonType) }
+ var showToast by remember { mutableStateOf(false) }
+ var toastMessage by remember { mutableStateOf("") }
+ var showDialog by remember { mutableStateOf(false) }
+ var dialogTitle by remember { mutableStateOf("") }
+ var dialogDescription by remember { mutableStateOf("") }
+ var pendingAction by remember { mutableStateOf<(() -> Unit)?>(null) }
- Column(
- Modifier
- .background(colors.Black)
- .fillMaxSize()
- .padding(start = 20.dp, end = 20.dp)
- ) {
- // TODO: 배경 이미지 추가
- Spacer(modifier = Modifier.height(20.dp))
+ Box(Modifier.fillMaxSize()) {
+ Image(
+ painter = painterResource(id = R.drawable.group_room_recruiting),
+ contentDescription = "배경 이미지",
+ modifier = Modifier
+ .align(Alignment.TopCenter)
+ .fillMaxWidth(),
+ contentScale = ContentScale.Crop
+ )
- //타이틀
- Row(
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text(
- text = detail.title,
- style = typography.bigtitle_b700_s22_h24,
- color = colors.White
+ // 그라데이션 페이드 오버레이 (상단과 하단이 더 어두움)
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(
+ Brush.verticalGradient(
+ colors = listOf(
+ colors.Black.copy(alpha = 1f),
+ colors.Black.copy(alpha = 0.3f),
+ colors.Black.copy(alpha = 1f),
+ colors.Black.copy(alpha = 1f),
+ colors.Black.copy(alpha = 1f),
+ colors.Black.copy(alpha = 1f)
+ ),
+ startY = 0f,
+ endY = Float.POSITIVE_INFINITY
)
- if (detail.isSecret) {
- Spacer(Modifier.width(2.dp))
- Icon(
- painter = painterResource(id = R.drawable.ic_lock),
- contentDescription = "비밀방",
- tint = colors.White
- )
- } else {
- Spacer(Modifier.width(2.dp))
- Icon(
- painter = painterResource(id = R.drawable.ic_unlock),
- contentDescription = "오픈방",
- tint = colors.White
- )
- }
- }
- Spacer(modifier = Modifier.height(32.dp))
-
- //소개글
- Text(
- text = stringResource(R.string.group_room_desc),
- style = typography.menu_sb600_s14_h24,
- color = colors.White,
)
+ )
- Text(
- text = detail.description,
- style = typography.copy_r400_s12_h20,
- color = colors.Grey,
- modifier = Modifier
- .padding(top = 5.dp, bottom = 18.dp)
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ ) {
+ Column {
+ DefaultTopAppBar(
+ isRightIconVisible = false,
+ isTitleVisible = false,
+ onLeftClick = onBackClick, // 뒤로가기 콜백 연결
)
-
- Row(
- Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
+ Column(
+ Modifier
+ .fillMaxSize()
+ .padding(start = 20.dp, end = 20.dp)
) {
- //모집 기간
- Column {
- Row(
- verticalAlignment = Alignment.CenterVertically
- ) {
- Icon(
- painter = painterResource(id = R.drawable.ic_calendar),
- contentDescription = "모임 활동기간",
- tint = colors.White
- )
- Spacer(Modifier.width(2.dp))
- Text(
- text = stringResource(R.string.group_period),
- style = typography.view_m500_s12_h20,
- color = colors.White
- )
- }
- Spacer(Modifier.height(12.dp))
+ Spacer(modifier = Modifier.height(20.dp))
+
+ //타이틀
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
Text(
- text = stringResource(
- R.string.group_room_period,
- detail.startDate,
- detail.endDate
- ),
- style = typography.info_r400_s12,
- color = colors.Grey
+ text = detail.title,
+ style = typography.bigtitle_b700_s22_h24,
+ color = colors.White
)
- }
-
- //참여 인원
- Column {
- Row(
- verticalAlignment = Alignment.CenterVertically
- ) {
+ if (detail.isSecret) {
+ Spacer(Modifier.width(2.dp))
Icon(
- painter = painterResource(id = R.drawable.ic_group),
- contentDescription = "참여 중인 독서 메이트",
+ painter = painterResource(id = R.drawable.ic_lock),
+ contentDescription = "비밀방",
tint = colors.White
)
+ } else {
Spacer(Modifier.width(2.dp))
- Text(
- text = stringResource(R.string.group_mate),
- style = typography.view_m500_s12_h20,
- color = colors.White
- )
- }
- Spacer(Modifier.height(12.dp))
- Row(
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text(
- text = stringResource(
- R.string.group_room_screen_participant_count,
- detail.members
- ),
- style = typography.info_r400_s12,
- color = colors.White
- )
- Spacer(Modifier.width(2.dp))
- Text(
- text = stringResource(
- R.string.group_room_screen_participant_count_max,
- detail.maxMembers
- ),
- style = typography.info_m500_s12,
- color = colors.Grey
+ Icon(
+ painter = painterResource(id = R.drawable.ic_unlock),
+ contentDescription = "오픈방",
+ tint = colors.White
)
}
}
- }
+ Spacer(modifier = Modifier.height(32.dp))
- Spacer(Modifier.height(16.dp))
- Row(
- verticalAlignment = Alignment.CenterVertically
- ) {
- // 모집 마감/장르
- Box(
- Modifier
- .background(colors.Grey03, shape = RoundedCornerShape(14.dp))
- .padding(horizontal = 12.dp, vertical = 8.dp)
+ //소개글
+ Text(
+ text = stringResource(R.string.group_room_desc),
+ style = typography.menu_sb600_s14_h24,
+ color = colors.White,
+ )
+
+ Text(
+ text = detail.description,
+ style = typography.copy_r400_s12_h20,
+ color = colors.Grey,
+ modifier = Modifier
+ .padding(top = 5.dp, bottom = 20.dp)
+ )
+
+ Row(
+ Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
) {
- Row {
- Text(
- text = stringResource(R.string.group_recruiting),
- style = typography.info_m500_s12,
- color = colors.White
- )
- Spacer(Modifier.width(4.dp))
+ //모집 기간
+ Column {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_calendar),
+ contentDescription = "모임 활동기간",
+ tint = colors.White
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = stringResource(R.string.group_period),
+ style = typography.view_m500_s12_h20,
+ color = colors.White
+ )
+ }
+ Spacer(Modifier.height(12.dp))
Text(
text = stringResource(
- R.string.group_room_screen_end_date,
- detail.daysLeft
+ R.string.group_room_period,
+ detail.startDate,
+ detail.endDate
),
- style = typography.info_m500_s12,
- color = colors.NeonGreen
+ style = typography.timedate_r400_s11,
+ color = colors.Grey
)
}
+ //참여 인원
+ Column(
+ verticalArrangement = Arrangement.Center,
+ modifier = Modifier.padding(end = 18.dp)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_group),
+ contentDescription = "참여 중인 독서 메이트",
+ tint = colors.White
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = stringResource(R.string.group_mate),
+ style = typography.view_m500_s12_h20,
+ color = colors.White
+ )
+ }
+ Spacer(Modifier.height(12.dp))
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(
+ R.string.group_room_screen_participant_count,
+ detail.members
+ ),
+ style = typography.menu_sb600_s12,
+ color = colors.White
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = stringResource(
+ R.string.group_room_screen_participant_count_max,
+ detail.maxMembers
+ ),
+ style = typography.info_m500_s12,
+ color = colors.Grey
+ )
+ }
+ }
}
- Spacer(Modifier.width(12.dp))
- Box(
- Modifier
- .background(colors.Grey03, shape = RoundedCornerShape(14.dp))
- .padding(horizontal = 12.dp, vertical = 8.dp)
+
+ Spacer(Modifier.height(16.dp))
+ Row(
+ verticalAlignment = Alignment.CenterVertically
) {
- Row {
- Text(
- text = stringResource(R.string.group_genre),
- style = typography.info_m500_s12,
- color = colors.White
- )
- Spacer(Modifier.width(4.dp))
- Text(
- text = detail.genre,
- style = typography.info_m500_s12,
- color = colors.genreColor
- )
+ // 모집 마감/장르
+ Box(
+ Modifier
+ .background(colors.Grey03, shape = RoundedCornerShape(14.dp))
+ .padding(horizontal = 12.dp, vertical = 8.dp)
+ ) {
+ Row {
+ Text(
+ text = stringResource(R.string.group_recruiting),
+ style = typography.info_m500_s12,
+ color = colors.White
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = stringResource(
+ R.string.group_room_screen_end_date,
+ detail.daysLeft
+ ),
+ style = typography.info_m500_s12,
+ color = colors.NeonGreen
+ )
+ }
+ }
+ Spacer(Modifier.width(12.dp))
+ Box(
+ Modifier
+ .background(colors.Grey03, shape = RoundedCornerShape(14.dp))
+ .padding(horizontal = 12.dp, vertical = 8.dp)
+ ) {
+ Row {
+ Text(
+ text = stringResource(R.string.group_genre),
+ style = typography.info_m500_s12,
+ color = colors.White
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = detail.genre,
+ style = typography.info_m500_s12,
+ color = colors.genreColor
+ )
+ }
}
-
}
- }
- Spacer(Modifier.height(30.dp))
+ Spacer(Modifier.height(30.dp))
- //읽을 책 정보
- CardRoomBook(
- title = detail.bookData.title,
- author = detail.bookData.author,
- publisher = detail.bookData.publisher,
- description = detail.bookData.description,
- imageRes = detail.bookData.imageRes
- )
+ //읽을 책 정보
+ CardRoomBook(
+ title = detail.bookData.title,
+ author = detail.bookData.author,
+ publisher = detail.bookData.publisher,
+ description = detail.bookData.description,
+ imageRes = detail.bookData.imageRes
+ )
- Spacer(Modifier.height(40.dp))
- Text(
- text = stringResource(R.string.group_recommend),
- style = typography.smalltitle_sb600_s18_h24,
- color = colors.White
- )
- Spacer(Modifier.height(24.dp))
+ Spacer(Modifier.height(40.dp))
+ Text(
+ text = stringResource(R.string.group_recommend),
+ style = typography.smalltitle_sb600_s18_h24,
+ color = colors.White
+ )
+ Spacer(Modifier.height(24.dp))
- //추천 모임방
- LazyRow(
- horizontalArrangement = Arrangement.spacedBy(20.dp)
- ) {
- items(detail.recommendations) { rec ->
- CardItemRoomSmall(
- title = rec.title,
- participants = rec.participants,
- maxParticipants = rec.maxParticipants,
- endDate = rec.endDate,
- imageRes = rec.imageRes,
- onClick = { onRecommendationClick(rec) }
- )
+ //추천 모임방
+ LazyRow(
+ horizontalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ items(detail.recommendations) { rec ->
+ CardItemRoomSmall(
+ title = rec.title,
+ participants = rec.participants,
+ maxParticipants = rec.maxParticipants,
+ endDate = rec.endDate,
+ imageRes = rec.imageRes,
+ onClick = { onRecommendationClick(rec) }
+ )
+ }
}
}
-
- Spacer(Modifier.weight(1f))
}
}
// 하단 버튼
- val buttonText = when (buttonType) {
+ val buttonText = when (currentButtonType) {
GroupBottomButtonType.JOIN -> stringResource(R.string.group_room_screen_participant)
GroupBottomButtonType.CANCEL -> stringResource(R.string.group_room_screen_cancel)
GroupBottomButtonType.CLOSE -> stringResource(R.string.group_room_screen_end)
}
- val buttonColor = when (buttonType) {
+ val buttonColor = when (currentButtonType) {
GroupBottomButtonType.JOIN -> colors.Purple
- GroupBottomButtonType.CANCEL -> colors.Red
- GroupBottomButtonType.CLOSE -> colors.Grey02
+ GroupBottomButtonType.CANCEL -> colors.Purple
+ GroupBottomButtonType.CLOSE -> colors.Purple
}
Button(
- onClick = onBottomButtonClick,
+ onClick = {
+ when (currentButtonType) {
+ GroupBottomButtonType.JOIN -> {
+ onParticipation() // 외부 콜백 호출
+ showToast = true
+ toastMessage = context.getString(R.string.group_participant_complete_alarm)
+ currentButtonType = GroupBottomButtonType.CANCEL
+ }
+
+ GroupBottomButtonType.CANCEL -> {
+ dialogTitle = context.getString(R.string.group_participant_cancel_popup)
+ dialogDescription =
+ context.getString(R.string.group_participant_cancel_comment)
+ pendingAction = {
+ onCancelParticipation()
+ showToast = true
+ toastMessage =
+ context.getString(R.string.group_participant_cancel_alarm)
+ currentButtonType = GroupBottomButtonType.JOIN
+ }
+ showDialog = true
+ }
+
+ GroupBottomButtonType.CLOSE -> {
+ dialogTitle = context.getString(R.string.group_participant_close_popup)
+ dialogDescription =
+ context.getString(R.string.group_participant_close_comment)
+ pendingAction = {
+ onCloseRecruitment()
+ showToast = true
+ toastMessage = context.getString(R.string.group_participant_close_alarm)
+ }
+ showDialog = true
+ }
+ }
+ },
colors = ButtonDefaults.buttonColors(
containerColor = buttonColor
),
@@ -299,12 +389,55 @@ fun GroupRoomScreen(
color = colors.White
)
}
+
+ // 토스트 팝업
+ if (showToast) {
+ ToastWithDate(
+ message = toastMessage,
+ modifier = Modifier
+ .align(Alignment.TopCenter)
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ .zIndex(2f)
+ )
+ }
+
+ // 다이얼로그 팝업
+ if (showDialog) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(colors.Black.copy(alpha = 0.5f))
+ .zIndex(3f),
+ contentAlignment = Alignment.Center
+ ) {
+ DialogPopup(
+ title = dialogTitle,
+ description = dialogDescription,
+ onConfirm = {
+ showDialog = false
+ pendingAction?.invoke()
+ },
+ onCancel = {
+ showDialog = false
+ pendingAction = null
+ }
+ )
+ }
+ }
+ }
+
+ // 토스트 3초
+ LaunchedEffect(showToast) {
+ if (showToast) {
+ delay(3000)
+ showToast = false
+ }
}
}
-@Preview()
+@Preview(name = "참여 버튼 상태")
@Composable
-fun GroupRoomDetailScreenPreview_AllCases() {
+fun GroupRoomScreenPreview_Join() {
ThipTheme {
val recommendations = listOf(
GroupCardItemRoomData(
@@ -353,14 +486,14 @@ fun GroupRoomDetailScreenPreview_AllCases() {
title = "심장보다 단단한 토마토 한 알",
author = "고선지",
publisher = "푸른출판사",
- description = "‘시집만 읽는 사람들’ 3월 모임에서 읽는 시집. 상처받고 단단해진 마음을 담은 감동적인 시와 해설이 어우러진 책으로, 읽는 이로 하여금 자신의 이야기를 투영하게 하는 힘이 있다.",
+ description = "'시집만 읽는 사람들' 3월 모임에서 읽는 시집. 상처받고 단단해진 마음을 담은 감동적인 시와 해설이 어우러진 책으로, 읽는 이로 하여금 자신의 이야기를 투영하게 하는 힘이 있다.",
imageRes = R.drawable.bookcover_sample
)
val detailJoin = GroupRoomData(
title = "시집만 읽는 사람들 3월",
isSecret = true,
- description = "‘시집만 읽는 사람들’ 3월 모임입니다. 이번 달 모임에서는 심장보다 단단한 토마토 한 알을 함께 읽어요.",
+ description = "'시집만 읽는 사람들' 3월 모임입니다. 이번 달 모임에서는 심장보다 단단한 토마토 한 알을 함께 읽어요.",
startDate = "2025.01.12",
endDate = "2025.02.12",
members = 22,
@@ -370,39 +503,145 @@ fun GroupRoomDetailScreenPreview_AllCases() {
bookData = bookData,
recommendations = recommendations
)
- val detailCancel = detailJoin.copy(
- title = "참여 중인 독서모임",
- isSecret = false,
- members = 17
- )
- val detailHost = detailJoin.copy(
- title = "내가 호스트인 독서모임",
- isSecret = false,
- members = 30,
- maxMembers = 30
+
+ GroupRoomScreen(
+ detail = detailJoin,
+ buttonType = GroupBottomButtonType.JOIN,
+ onRecommendationClick = {},
+ onParticipation = {},
+ onCancelParticipation = {},
+ onCloseRecruitment = {},
+ onBackClick = {}
)
+ }
+}
- Column(verticalArrangement = Arrangement.spacedBy(32.dp)) {
- // 1. 참여 가능한 경우(참여하기)
- GroupRoomScreen(
- detail = detailJoin,
- buttonType = GroupBottomButtonType.JOIN,
- onBottomButtonClick = {}
- )
- // 2. 참여 중인 경우(참여 취소하기)
- GroupRoomScreen(
- detail = detailCancel,
- buttonType = GroupBottomButtonType.CANCEL,
- onBottomButtonClick = {}
- )
- // 3. 내가 호스트인 경우(모집 마감하기)
- GroupRoomScreen(
- detail = detailHost,
- buttonType = GroupBottomButtonType.CLOSE,
- onBottomButtonClick = {}
+@Preview(name = "참여 취소 버튼 상태")
+@Composable
+fun GroupRoomScreenPreview_Cancel() {
+ ThipTheme {
+ val recommendations = listOf(
+ GroupCardItemRoomData(
+ title = "일본 소설 좋아하는 사람들 일본 소설 좋아하는 사람들",
+ participants = 19,
+ maxParticipants = 25,
+ isRecruiting = true,
+ endDate = 2,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "일본 소설 좋아하는 사람들 일본 소설 좋아하는 사람들",
+ participants = 12,
+ maxParticipants = 16,
+ isRecruiting = true,
+ endDate = 6,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "에세이 나눔방",
+ participants = 14,
+ maxParticipants = 20,
+ isRecruiting = true,
+ endDate = 4,
+ genreIndex = 0
)
- }
+ )
+
+ val bookData = GroupBookData(
+ title = "심장보다 단단한 토마토 한 알",
+ author = "고선지",
+ publisher = "푸른출판사",
+ description = "'시집만 읽는 사람들' 3월 모임에서 읽는 시집. 상처받고 단단해진 마음을 담은 감동적인 시와 해설이 어우러진 책으로, 읽는 이로 하여금 자신의 이야기를 투영하게 하는 힘이 있다.",
+ imageRes = R.drawable.bookcover_sample
+ )
+
+ val detailCancel = GroupRoomData(
+ title = "시집만 읽는 사람들 3월",
+ isSecret = true,
+ description = "'시집만 읽는 사람들' 3월 모임입니다. 이번 달 모임에서는 심장보다 단단한 토마토 한 알을 함께 읽어요.",
+ startDate = "2025.01.12",
+ endDate = "2025.02.12",
+ members = 23, // 참여 후 인원 증가
+ maxMembers = 30,
+ daysLeft = 4,
+ genre = "고전 문학",
+ bookData = bookData,
+ recommendations = recommendations
+ )
+
+ GroupRoomScreen(
+ detail = detailCancel,
+ buttonType = GroupBottomButtonType.CANCEL,
+ onRecommendationClick = {},
+ onParticipation = {},
+ onCancelParticipation = {},
+ onCloseRecruitment = {},
+ onBackClick = {}
+ )
}
}
+@Preview(name = "모집 마감 버튼 상태")
+@Composable
+fun GroupRoomScreenPreview_Close() {
+ ThipTheme {
+ val recommendations = listOf(
+ GroupCardItemRoomData(
+ title = "일본 소설 좋아하는 사람들 일본 소설 좋아하는 사람들",
+ participants = 19,
+ maxParticipants = 25,
+ isRecruiting = true,
+ endDate = 2,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "일본 소설 좋아하는 사람들 일본 소설 좋아하는 사람들",
+ participants = 12,
+ maxParticipants = 16,
+ isRecruiting = true,
+ endDate = 6,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "미스터리 소설 탐구",
+ participants = 8,
+ maxParticipants = 15,
+ isRecruiting = true,
+ endDate = 3,
+ genreIndex = 0
+ )
+ )
+
+ val bookData = GroupBookData(
+ title = "심장보다 단단한 토마토 한 알",
+ author = "고선지",
+ publisher = "푸른출판사",
+ description = "'시집만 읽는 사람들' 3월 모임에서 읽는 시집. 상처받고 단단해진 마음을 담은 감동적인 시와 해설이 어우러진 책으로, 읽는 이로 하여금 자신의 이야기를 투영하게 하는 힘이 있다.",
+ imageRes = R.drawable.bookcover_sample
+ )
+
+ val detailClose = GroupRoomData(
+ title = "시집만 읽는 사람들 3월",
+ isSecret = false, // 오픈방으로 변경
+ description = "'시집만 읽는 사람들' 3월 모임입니다. 이번 달 모임에서는 심장보다 단단한 토마토 한 알을 함께 읽어요. 모임장이 모집을 마감할 수 있는 상태입니다.",
+ startDate = "2025.01.12",
+ endDate = "2025.02.12",
+ members = 15, // 적절한 인원
+ maxMembers = 30,
+ daysLeft = 7, // 마감일이 조금 더 남음
+ genre = "고전 문학",
+ bookData = bookData,
+ recommendations = recommendations
+ )
+ GroupRoomScreen(
+ detail = detailClose,
+ buttonType = GroupBottomButtonType.CLOSE,
+ onRecommendationClick = {},
+ onParticipation = {},
+ onCancelParticipation = {},
+ onCloseRecruitment = {},
+ onBackClick = {}
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomSecretScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomSecretScreen.kt
new file mode 100644
index 00000000..3cd62a38
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomSecretScreen.kt
@@ -0,0 +1,169 @@
+package com.texthip.thip.ui.group.myroom.screen
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+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.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+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.SingleDigitBox
+import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar
+import com.texthip.thip.ui.theme.ThipTheme
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun GroupRoomSecretScreen(
+ onBackClick: () -> Unit = {},
+ onPasswordComplete: (String) -> Unit = {},
+ correctPassword: String = "1234" // 실제로는 외부에서 받아올 값
+) {
+ var password by remember { mutableStateOf(arrayOf("", "", "", "")) }
+ var showError by remember { mutableStateOf(false) }
+ val focusRequesters = remember { List(4) { FocusRequester() } }
+ val keyboardController = LocalSoftwareKeyboardController.current
+
+ // 비밀번호가 4자리 완성되면 자동으로 검증
+ LaunchedEffect(password.toList()) {
+ val fullPassword = password.joinToString("")
+ if (fullPassword.length == 4 && password.all { it.length == 1 }) {
+ if (fullPassword == correctPassword) {
+ showError = false
+ onPasswordComplete(fullPassword)
+ } else {
+ showError = true
+ kotlinx.coroutines.delay(1000)
+ password = arrayOf("", "", "", "")
+ showError = false
+ focusRequesters[0].requestFocus()
+ }
+ } else {
+ showError = false
+ }
+ }
+
+ // 화면 진입 시 키보드 자동 포커스
+ LaunchedEffect(Unit) {
+ focusRequesters[0].requestFocus()
+ keyboardController?.show()
+ }
+
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize()
+ ) {
+ // 상단 앱바
+ DefaultTopAppBar(
+ isRightIconVisible = false,
+ isTitleVisible = false,
+ onLeftClick = onBackClick,
+ )
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(modifier = Modifier.height(160.dp))
+
+ Text(
+ text = stringResource(R.string.group_secret_screen_comment),
+ style = typography.smalltitle_sb600_s18_h24,
+ color = colors.White,
+ textAlign = TextAlign.Center
+ )
+
+ Spacer(modifier = Modifier.height(32.dp))
+
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ repeat(4) { index ->
+ SingleDigitBox(
+ value = password[index],
+ onValueChange = { input ->
+ if (input.length <= 1 && input.all { it.isDigit() }) {
+ val newPassword = password.copyOf()
+ newPassword[index] = input
+ password = newPassword
+
+ if (input.isNotEmpty() && index < 3) {
+ focusRequesters[index + 1].requestFocus()
+ }
+ }
+ },
+ onBackspace = {
+ if (password[index].isEmpty() && index > 0) {
+ val newPassword = password.copyOf()
+ newPassword[index - 1] = ""
+ password = newPassword
+ focusRequesters[index - 1].requestFocus()
+ }
+ },
+ containerColor = colors.DarkGrey50,
+ borderColor = if (showError) colors.Red else Color.Transparent,
+ modifier = Modifier
+ .size(44.dp)
+ .focusRequester(focusRequesters[index])
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(12.dp))
+
+ // 에러 메시지
+ if (showError) {
+ Text(
+ text = stringResource(R.string.group_secret_screen_error_comment),
+ style = typography.menu_r400_s14_h24,
+ color = colors.Red,
+ textAlign = TextAlign.Center
+ )
+ } else {
+ Spacer(modifier = Modifier.height(20.dp))
+ }
+
+ Spacer(modifier = Modifier.weight(1f))
+ }
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+fun GroupRoomSecretScreenPreview() {
+ ThipTheme {
+ GroupRoomSecretScreen(
+ onBackClick = {},
+ onPasswordComplete = { password ->
+ },
+ correctPassword = "1234"
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupSearchScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupSearchScreen.kt
new file mode 100644
index 00000000..43ec959c
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupSearchScreen.kt
@@ -0,0 +1,197 @@
+package com.texthip.thip.ui.group.myroom.screen
+
+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.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+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.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.platform.LocalFocusManager
+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.FilterButton
+import com.texthip.thip.ui.common.forms.SearchBookTextField
+import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar
+import com.texthip.thip.ui.group.myroom.mock.GroupCardItemRoomData
+import com.texthip.thip.ui.theme.ThipTheme
+
+@Composable
+fun GroupSearchScreen(
+ modifier: Modifier = Modifier
+) {
+ var recentSearches by rememberSaveable {
+ mutableStateOf(listOf("user.02", "ㅇㅇ", "훽후ㅣㅣ", "검색4", "검색5", "검색6"))
+ }
+ var searchText by rememberSaveable { mutableStateOf("") }
+ var isSearched by rememberSaveable { mutableStateOf(false) }
+ var selectedGenreIndex by rememberSaveable { mutableIntStateOf(-1) }
+ var selectedSortOption by rememberSaveable { mutableStateOf("마감임박순") }
+ val focusRequester = remember { FocusRequester() }
+ val focusManager = LocalFocusManager.current
+
+ // 샘플 장르, 정렬 옵션
+ val genres = listOf("문학", "과학·IT", "사회과학", "인문학", "예술")
+ val sortOptions = listOf("마감임박순", "최신순", "참여많은순")
+
+ // 샘플 모임방 리스트
+ val roomList = listOf(
+ GroupCardItemRoomData("aaa", 22, 30, true, 3, R.drawable.bookcover_sample, 0),
+ GroupCardItemRoomData("bbb", 15, 20, true, 7, R.drawable.bookcover_sample, 1, true),
+ GroupCardItemRoomData("ccc", 10, 15, true, 5, R.drawable.bookcover_sample, 2, true),
+ GroupCardItemRoomData("ddd", 8, 12, false, 2, R.drawable.bookcover_sample, 3, true),
+ GroupCardItemRoomData("eee", 18, 25, true, 4, R.drawable.bookcover_sample, 4),
+ GroupCardItemRoomData("fff", 12, 20, true, 1, R.drawable.bookcover_sample, 0),
+ GroupCardItemRoomData("ggg", 10, 14, true, 6, R.drawable.bookcover_sample, 1),
+ )
+
+ val liveFilteredRoomList by remember(searchText) {
+ derivedStateOf {
+ if (searchText.isBlank()) emptyList() else
+ roomList.filter { room ->
+ room.title.contains(searchText, ignoreCase = true)
+ }
+ }
+ }
+
+ val filteredRoomList by remember(searchText, selectedGenreIndex, selectedSortOption, isSearched) {
+ derivedStateOf {
+ if (!isSearched) emptyList()
+ else {
+ val filtered = roomList.filter { room ->
+ (searchText.isBlank() || room.title.contains(searchText, ignoreCase = true)) &&
+ (selectedGenreIndex == -1 || room.genreIndex == selectedGenreIndex)
+ }
+ when (selectedSortOption) {
+ "마감임박순" -> filtered.sortedBy { it.endDate }
+ "최신순" -> filtered // TODO: 생성일 기준 정렬 필요
+ "참여많은순" -> filtered.sortedByDescending { it.participants }
+ else -> filtered
+ }
+ }
+ }
+ }
+
+ LaunchedEffect(isSearched) {
+ if (isSearched) {
+ focusManager.clearFocus()
+ }
+ }
+
+ Box(
+ modifier = modifier.fillMaxSize()
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize()
+ ) {
+ DefaultTopAppBar(
+ title = stringResource(R.string.group_room_search_topappbar),
+ onLeftClick = {},
+ )
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 20.dp)
+ ) {
+ Spacer(modifier = Modifier.height(16.dp))
+
+ SearchBookTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .focusRequester(focusRequester),
+ hint = stringResource(R.string.group_room_search_hint),
+ text = searchText,
+ onValueChange = {
+ searchText = it
+ isSearched = false
+ },
+ onSearch = { query ->
+ if (query.isNotBlank() && !recentSearches.contains(query)) {
+ recentSearches = listOf(query) + recentSearches
+ }
+ isSearched = true
+ selectedGenreIndex = -1
+ }
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+
+ when {
+ searchText.isBlank() && !isSearched && recentSearches.isEmpty() -> {
+ GroupRecentSearchScreen(
+ recentSearches = emptyList(),
+ onSearchClick = {},
+ onRemove = {}
+ )
+ }
+ searchText.isBlank() && !isSearched && recentSearches.isNotEmpty() -> {
+ GroupRecentSearchScreen(
+ recentSearches = recentSearches,
+ onSearchClick = { keyword ->
+ searchText = keyword
+ isSearched = true
+ },
+ onRemove = { keyword ->
+ recentSearches = recentSearches.filterNot { it == keyword }
+ }
+ )
+ }
+ searchText.isNotBlank() && !isSearched -> {
+ if (liveFilteredRoomList.isEmpty()) {
+ GroupEmptyResultScreen(
+ mainText = stringResource(R.string.group_no_search_result1),
+ subText = stringResource(R.string.group_no_search_result2)
+ )
+ } else {
+ GroupLiveSearchResultScreen(
+ roomList = liveFilteredRoomList
+ )
+ }
+ }
+ isSearched -> {
+ GroupFilteredSearchResultScreen(
+ genres = genres,
+ selectedGenreIndex = selectedGenreIndex,
+ onGenreSelect = { selectedGenreIndex = it },
+ resultCount = filteredRoomList.size,
+ roomList = filteredRoomList,
+ )
+ }
+ }
+ }
+ }
+
+ if (isSearched) {
+ FilterButton(
+ modifier = Modifier
+ .align(Alignment.TopEnd)
+ .padding(top = 176.dp, end = 20.dp),
+ selectedOption = selectedSortOption,
+ options = sortOptions,
+ onOptionSelected = { selectedSortOption = it }
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+fun PreviewGroupSearchScreen() {
+ ThipTheme {
+ GroupSearchScreen()
+ }
+}
diff --git a/app/src/main/java/com/texthip/thip/ui/group/screen/GroupDoneScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/screen/GroupDoneScreen.kt
new file mode 100644
index 00000000..68a6483f
--- /dev/null
+++ b/app/src/main/java/com/texthip/thip/ui/group/screen/GroupDoneScreen.kt
@@ -0,0 +1,137 @@
+package com.texthip.thip.ui.group.screen
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+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.cards.CardItemRoom
+import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar
+import com.texthip.thip.ui.group.myroom.mock.GroupCardItemRoomData
+import com.texthip.thip.ui.theme.ThipTheme
+import com.texthip.thip.ui.theme.ThipTheme.colors
+import com.texthip.thip.ui.theme.ThipTheme.typography
+
+@Composable
+fun GroupDoneScreen(
+ name: String,
+ allDataList: List,
+ onCardClick: (GroupCardItemRoomData) -> Unit = {}
+) {
+ // isRecruiting == false 인 방만 필터링 (혹시 몰라서 넣어둡니다)
+ val doneList = remember(allDataList) {
+ allDataList.filter { !it.isRecruiting }
+ }
+
+ Column(
+ Modifier
+ .fillMaxSize()
+ ) {
+ DefaultTopAppBar(
+ title = stringResource(R.string.group_done_title),
+ onLeftClick = {},
+ )
+ Column(
+ Modifier
+ .background(colors.Black)
+ .fillMaxSize()
+ .padding(horizontal = 20.dp)
+ ) {
+
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ contentPadding = PaddingValues(bottom = 20.dp),
+ modifier = Modifier
+ .fillMaxSize()
+ ) {
+
+ item {
+ Text (
+ text = stringResource(R.string.group_done_user_comment, name),
+ color = colors.White,
+ style = typography.menu_r400_s14_h24
+ )
+ }
+
+ items(doneList) { item ->
+ CardItemRoom(
+ title = item.title,
+ participants = item.participants,
+ maxParticipants = item.maxParticipants,
+ isRecruiting = item.isRecruiting,
+ imageRes = item.imageRes,
+ onClick = { onCardClick(item) }
+ )
+ }
+ }
+ }
+ }
+}
+
+
+
+@Preview()
+@Composable
+fun MyGroupListFilterScreenPreview() {
+ ThipTheme {
+ val dataList = listOf(
+ GroupCardItemRoomData(
+ title = "모임방 이름입니다. 모임방...",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = false,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "모임방 이름입니다. 모임방...",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = false,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "모임방 이름입니다. 모임방...",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = false,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "모임방 이름입니다. 모임방...",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = false,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "모임방 이름입니다. 모임방...",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = false,
+ genreIndex = 0
+ ),
+ GroupCardItemRoomData(
+ title = "모임방 이름입니다. 모임방...",
+ participants = 22,
+ maxParticipants = 30,
+ isRecruiting = false,
+ genreIndex = 0
+ )
+ )
+
+ GroupDoneScreen(
+ name = "rbqks529",
+ allDataList = dataList)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt
index e9746096..ca393b6e 100644
--- a/app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt
+++ b/app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt
@@ -14,7 +14,9 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
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.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
@@ -41,6 +43,7 @@ fun GroupScreen(
val myGroups by viewModel.myGroups.collectAsState()
val roomSections by viewModel.roomSections.collectAsState()
val scrollState = rememberScrollState()
+ var searchText by remember { mutableStateOf("") }
Box(
Modifier
@@ -62,7 +65,11 @@ fun GroupScreen(
Spacer(Modifier.height(16.dp))
// 검색창
- GroupSearchTextField(onValueChange = { })
+ GroupSearchTextField(
+ value = searchText,
+ onValueChange = {},
+ onClick = {}
+ )
Spacer(Modifier.height(32.dp))
// 내 모임방 헤더 + 카드
@@ -75,7 +82,7 @@ fun GroupScreen(
groupCards = myGroups,
onCardClick = { viewModel.onMyGroupCardClick(it) }
)
- Spacer(Modifier.height(40.dp))
+ Spacer(Modifier.height(32.dp))
Spacer(
Modifier
diff --git a/app/src/main/res/drawable/group_room_recruiting.png b/app/src/main/res/drawable/group_room_recruiting.png
new file mode 100644
index 00000000..bf8347eb
Binary files /dev/null and b/app/src/main/res/drawable/group_room_recruiting.png differ
diff --git a/app/src/main/res/drawable/ic_secret_cover.xml b/app/src/main/res/drawable/ic_secret_cover.xml
new file mode 100644
index 00000000..3c30cad1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_secret_cover.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thip_empty_room.xml b/app/src/main/res/drawable/ic_thip_empty_room.xml
new file mode 100644
index 00000000..25b6bfb0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thip_empty_room.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c2ab39ae..e61783ec 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -42,7 +42,7 @@
"%1$s일 뒤 "
모집 마감
종료
- %1$s명
+ %1$s명
참여
%1$s
/ %1$s명
@@ -164,6 +164,21 @@
\@
\
00님에게 댓글을 남겨보세요.
+ 완료된 모임방
+ %1$s님이 참여했던 모임방들을 확인해보세요.
+ " 방 제목 혹은 책 제목을 검색해보세요."
+ 최근 검색어
+ 최근 검색어가 아직 없어요.
+ 모임 검색
+ 전체 %1$d
+ 해당하는 모임방이 없어요
+ 직접 모임방을 만들어보세요.
+ 비밀번호를 입력해주세요.
+ 비밀번호가 일치하지 않습니다.
+ 모임방 참여가 완료되었어요!
+ 참여를 취소하시겠어요?
+ 참여 취소 후 다시 참여할 수 있어요.
+ 모임방 참여가 취소되었어요! 다른 방을 찾아보세요.
피드
@@ -175,7 +190,7 @@
모임 만들기
저장한 책
모임 책
- 책 제목, 저자 검색
+ 책 제목을 검색해보세요.
책 등록하기
현재 등록된 책이 아닙니다.\n원하시는 책을 신청해주세요.
책 신청
@@ -230,5 +245,14 @@
%1$s 님을 구독했어요
%1$s 님을 구독취소했어요
구독취소
+ 모집을 마감하시겠습니까?
+ 독서메이트 모집을 마감하면\n지금 바로 모임방 활동을 바로 시작할 수 있어요.
+ 독서메이트 모집을 성공적으로 마감했어요.
+ 모임방이 아직 없어요
+ 해당 장르의 모임방이 생기면 보여줄게요!
+ 참여 중인 모임방이 없어요
+ 모임방을 찾아 참여해보세요!
+ 참여 중인 모임방이 없어요
+ 첫번째 모임방에 참여해보세요
\ No newline at end of file