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 @@ + + + + + + + + + + + + + \ 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