diff --git a/app/src/main/java/com/texthip/thip/ui/common/header/AuthorHeader.kt b/app/src/main/java/com/texthip/thip/ui/common/header/AuthorHeader.kt index f8731155..36cef65b 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/header/AuthorHeader.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/header/AuthorHeader.kt @@ -1,6 +1,7 @@ package com.texthip.thip.ui.common.header import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -41,7 +42,8 @@ fun AuthorHeader( showThipNum: Boolean = false, thipNum: Int? = null, profileImageSize: Dp = 54.dp, - onButtonClick: () -> Unit = {} + onButtonClick: () -> Unit = {}, + onThipNumClick: () -> Unit = {} ) { Row( modifier = modifier @@ -84,31 +86,31 @@ fun AuthorHeader( } if (showButton) { OutlinedButton( - modifier = Modifier - .then( - if (buttonWidth != null) - Modifier - .width(buttonWidth) - .height(33.dp) - else Modifier - ), - text = buttonText, - textStyle = typography.view_m500_s14, - onClick = onButtonClick + modifier = Modifier + .width(buttonWidth) + .height(33.dp), + text = buttonText, + textStyle = typography.view_m500_s14, + onClick = onButtonClick ) } - if(showThipNum && thipNum!=null){ - Text( - text = stringResource(R.string.thip_num,thipNum), - style = typography.view_r400_s11_h20, - color = colors.White - ) - Spacer(modifier = Modifier.width(18.dp)) - Icon( - painter = painterResource(R.drawable.ic_chevron), - contentDescription = null, - tint = colors.White, - ) + if (showThipNum && thipNum != null) { + Row( + modifier = Modifier.clickable(onClick = onThipNumClick), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(R.string.thip_num, thipNum)+ stringResource(R.string.thip_ing), + style = typography.view_r400_s11_h20, + color = colors.White + ) + Spacer(modifier = Modifier.width(18.dp)) + Icon( + painter = painterResource(R.drawable.ic_chevron), + contentDescription = null, + tint = colors.White, + ) + } } } } @@ -131,7 +133,10 @@ fun PreviewAuthorHeader() { badgeText = "칭호칭호칭호", showButton = false, showThipNum = true, - thipNum = 10 + thipNum = 10, + onThipNumClick = { + + } ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/feed/component/FeedSubscribelistBar.kt b/app/src/main/java/com/texthip/thip/ui/feed/component/FeedSubscribelistBar.kt index 30e5022f..ebed653d 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/component/FeedSubscribelistBar.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/component/FeedSubscribelistBar.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight @@ -57,14 +58,13 @@ fun FeedSubscribeBarlist( color = colors.White ) ) { - append("${followerProfileImageUrls.size}명") - } + append(stringResource(R.string.thip_num, followerProfileImageUrls.size)) } withStyle( style = SpanStyle( color = colors.Grey ) ) { - append("이 띱 하는중") + append(stringResource(R.string.thip_ing)) } }, style = typography.info_r400_s12, @@ -74,25 +74,28 @@ fun FeedSubscribeBarlist( Row( verticalAlignment = Alignment.CenterVertically ) { - followerProfileImageUrls.take(5).reversed().forEachIndexed { index, imageUrl -> - AsyncImage( - model = imageUrl, + if (followerProfileImageUrls.isNotEmpty()) { + val previewList = followerProfileImageUrls.take(5) + previewList.reversed().forEachIndexed { index, imageUrl -> + AsyncImage( + model = imageUrl, + contentDescription = null, + modifier = Modifier + .size(24.dp) + .clip(CircleShape) + .background(Color.LightGray) + ) + + val isLast = index == previewList.lastIndex + Spacer(modifier = Modifier.width(if (isLast) 15.dp else 4.dp)) + } + + Icon( + painter = painterResource(id = R.drawable.ic_chevron), contentDescription = null, - modifier = Modifier - .size(24.dp) - .clip(CircleShape) - .background(Color.LightGray) + tint = colors.Grey ) - - val isLast = index == followerProfileImageUrls.take(5).lastIndex - Spacer(modifier = Modifier.width(if (isLast) 15.dp else 4.dp)) } - - Icon( - painter = painterResource(id = R.drawable.ic_chevron), - contentDescription = null, - tint = colors.Grey - ) } } } diff --git a/app/src/main/java/com/texthip/thip/ui/feed/component/LiveSearchPeopleResult.kt b/app/src/main/java/com/texthip/thip/ui/feed/component/LiveSearchPeopleResult.kt new file mode 100644 index 00000000..438aeb9c --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/feed/component/LiveSearchPeopleResult.kt @@ -0,0 +1,90 @@ +package com.texthip.thip.ui.feed.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +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.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +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.common.header.AuthorHeader +import com.texthip.thip.ui.feed.mock.MySubscriptionData +import com.texthip.thip.ui.theme.ThipTheme +import com.texthip.thip.ui.theme.ThipTheme.colors + +@Composable +fun SearchPeopleResult( + modifier: Modifier = Modifier, + peopleList: List, + onThipNumClick: (MySubscriptionData) -> Unit = {} +) { + LazyColumn (modifier = modifier){ + itemsIndexed(peopleList) { index, user -> + AuthorHeader( + profileImage = user.profileImageUrl, + nickname = user.nickname, + badgeText = user.role, + profileImageSize = 36.dp, + showButton = false, + showThipNum = true, + thipNum = user.subscriberCount, + onThipNumClick = { onThipNumClick(user) } + ) + if (index < peopleList.size - 1) { + Spacer( + modifier = Modifier + .padding(vertical = 12.dp, horizontal = 20.dp) + .fillMaxWidth() + .height(1.dp) + .background(colors.DarkGrey02) + ) + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun SearchPeopleResultPreview() { + ThipTheme { + Box( + modifier = Modifier + .fillMaxSize() + ) { + SearchPeopleResult( + peopleList = listOf( + MySubscriptionData( + profileImageUrl = null, + nickname = "Thiper_Official", + role = "공식 인플루언서", + roleColor = colors.NeonGreen, + subscriberCount = 50, + isSubscribed = false + ), + MySubscriptionData( + profileImageUrl = null, + nickname = "Thiper_Writer", + role = "작가", + roleColor = colors.Yellow, + subscriberCount = 120, + isSubscribed = true + ), + MySubscriptionData( + profileImageUrl = null, + nickname = "Thiper_Newbie", + role = "칭호칭호", + roleColor = colors.White, + subscriberCount = 0, + isSubscribed = false + ) + ) + ) + } + } +} diff --git a/app/src/main/java/com/texthip/thip/ui/feed/component/MySubscribelistBar.kt b/app/src/main/java/com/texthip/thip/ui/feed/component/MySubscribelistBar.kt index d8b4125e..ca883a4e 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/component/MySubscribelistBar.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/component/MySubscribelistBar.kt @@ -2,6 +2,7 @@ package com.texthip.thip.ui.feed.component import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -12,6 +13,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -74,47 +76,82 @@ fun MySubscribeBarlist( Spacer(modifier = Modifier.height(4.dp)) - Row( - modifier = Modifier - .fillMaxWidth() - .height(58.dp) - .clickable { onClick() }, - verticalAlignment = Alignment.CenterVertically - ) { - subscriptions.take(maxVisibleCount).forEach { profile -> - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.width(36.dp) - ) { - AsyncImage( - model = profile.profileImageUrl, - contentDescription = null, - modifier = Modifier - .size(36.dp) - .clip(CircleShape) - .background(Color.LightGray) - ) - Text( - text = profile.nickname, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - style = typography.view_r400_s11_h20, - color = colors.White, + if (subscriptions.isEmpty()) { + EmptyMySubscriptionBar() + } else { + Row( + modifier = Modifier + .fillMaxWidth() + .height(58.dp) + .clickable { onClick() }, + verticalAlignment = Alignment.CenterVertically + ) { + subscriptions.take(maxVisibleCount).forEach { profile -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.width(36.dp) - ) + ) { + AsyncImage( + model = profile.profileImageUrl, + contentDescription = null, + modifier = Modifier + .size(36.dp) + .clip(CircleShape) + .background(Color.LightGray) + ) + Text( + text = profile.nickname, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = typography.view_r400_s11_h20, + color = colors.White, + modifier = Modifier.width(36.dp) + ) + } + Spacer(modifier = Modifier.width(12.dp)) } - Spacer(modifier = Modifier.width(12.dp)) + Spacer(modifier = Modifier.weight(1f)) + Icon( + painter = painterResource(id = R.drawable.ic_chevron), + contentDescription = null, + tint = colors.Grey + ) } - Spacer(modifier = Modifier.weight(1f)) - Icon( - painter = painterResource(id = R.drawable.ic_chevron), - contentDescription = null, - tint = colors.Grey - ) } } } } +@Composable +private fun EmptyMySubscriptionBar() { + Box( + modifier = Modifier + .fillMaxWidth() + .height(42.dp) + .clip(RoundedCornerShape(8.dp)) + .background(colors.DarkGrey02) + .clickable { } + ) { + Text( + modifier = Modifier + .align(Alignment.CenterStart) + .padding(start = 12.dp), + text = stringResource(R.string.find_thip_mate), + color = colors.White, + style = typography.view_m500_s12_h20 + ) + + Icon( + painter = painterResource(id = R.drawable.search_character_image), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(end = 12.dp) + .width(32.dp) + .height(42.dp) + ) + } +} @Preview @Composable @@ -138,4 +175,20 @@ private fun MySubscribeBarlistPrev() { } } +} + +@Preview +@Composable +private fun MySubscribeBarlistWithoutDataPrev() { + ThipTheme { + val previewData = emptyList() + + Column { + MySubscribeBarlist( + subscriptions = previewData, + onClick = {} + ) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/feed/component/PeopleRecentSearch.kt b/app/src/main/java/com/texthip/thip/ui/feed/component/PeopleRecentSearch.kt new file mode 100644 index 00000000..51f54663 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/feed/component/PeopleRecentSearch.kt @@ -0,0 +1,105 @@ +package com.texthip.thip.ui.feed.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.texthip.thip.R +import com.texthip.thip.ui.common.buttons.GenreChipButton +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 PeopleRecentSearch( + modifier: Modifier = Modifier, + recentSearches: List, + onSearchClick: (String) -> Unit, + onRemove: (String) -> Unit +) { + Column (modifier = modifier){ + Text( + text = stringResource(R.string.group_recent_search), + color = colors.White, + style = typography.smalltitle_sb600_s18_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) } + ) + } + } + } + } +} + +@Preview +@Composable +fun PeopleRecentSearchPrev() { + ThipTheme { + Box( + modifier = Modifier + .padding(16.dp) + ) { + var searches by remember { + mutableStateOf(listOf("thip", "걔누구더라", "으아아아", "ㅇㅇ", "user.02")) + } + + PeopleRecentSearch( + recentSearches = searches, + onSearchClick = { keyword -> + }, + onRemove = { keyword -> + searches = searches.filter { it != keyword } + } + ) + } + } +} + +@Preview +@Composable +fun PeopleRecentSearchEmptyPrev() { + ThipTheme { + Box( + modifier = Modifier + .background(colors.Black) + .padding(16.dp) + ) { + PeopleRecentSearch( + recentSearches = emptyList(), + onSearchClick = { }, + onRemove = { } + ) + } + } +} diff --git a/app/src/main/java/com/texthip/thip/ui/feed/component/SearchPeopleEmptyResult.kt b/app/src/main/java/com/texthip/thip/ui/feed/component/SearchPeopleEmptyResult.kt new file mode 100644 index 00000000..611f946f --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/feed/component/SearchPeopleEmptyResult.kt @@ -0,0 +1,47 @@ +package com.texthip.thip.ui.feed.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +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 SearchPeopleEmptyResult( + modifier: Modifier= Modifier, + mainText: String +) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = mainText, + color = colors.White, + style = typography.smalltitle_sb600_s18_h24 + ) + } +} + +@Preview(showBackground = true) +@Composable +fun SearchPeopleEmptyResultPrev() { + ThipTheme { + Box( + modifier = Modifier + .fillMaxSize() + ) { + SearchPeopleEmptyResult( + mainText = "검색 결과가 없습니다" + ) + } + } +} diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt new file mode 100644 index 00000000..c174f666 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt @@ -0,0 +1,193 @@ +package com.texthip.thip.ui.feed.screen + +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.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.texthip.thip.R +import com.texthip.thip.ui.common.header.AuthorHeader +import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar +import com.texthip.thip.ui.feed.component.FeedSubscribeBarlist +import com.texthip.thip.ui.feed.component.MyFeedCard +import com.texthip.thip.ui.mypage.mock.FeedItem +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 FeedOthersScreen( + nickname: String, + userRole: String, + feeds: List = emptyList(), + totalFeedCount: Int = 0, + followerProfileImageUrls: List = emptyList() +) { + val feedStateList = remember { + mutableStateListOf().apply { + addAll(feeds) + } + } + Box(modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier.fillMaxSize() + ) { + DefaultTopAppBar( + isRightIconVisible = false, + isTitleVisible = false, + onLeftClick = {}, + ) + // 스크롤 영역 + LazyColumn( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + item { + Spacer(modifier = Modifier.height(32.dp)) + AuthorHeader( + profileImage = null, + nickname = nickname, + badgeText = userRole, + buttonText = stringResource(R.string.thip), + onButtonClick = {}, + modifier = Modifier.padding(bottom = 20.dp) + ) + Spacer(modifier = Modifier.height(16.dp)) + FeedSubscribeBarlist( + modifier = Modifier.padding(horizontal = 20.dp), + followerProfileImageUrls = followerProfileImageUrls, + onClick = { + } + ) + Spacer(modifier = Modifier.height(40.dp)) + Text( + text = stringResource(R.string.whole_num, totalFeedCount), + style = typography.menu_m500_s14_h24, + color = colors.Grey, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp, start = 20.dp) + ) + HorizontalDivider( + color = colors.DarkGrey02, + thickness = 1.dp, + modifier = Modifier.padding(horizontal = 20.dp) + ) + } + if (feedStateList.isEmpty()) { + item { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(top = 244.dp), + contentAlignment = Alignment.TopCenter + ) { + Text( + text = stringResource(R.string.empty_feed), + style = typography.smalltitle_sb600_s18_h24, + color = colors.White + ) + } + } + } else { + itemsIndexed(feedStateList, key = { _, item -> item.id }) { index, feed -> + Spacer(modifier = Modifier.height(if (index == 0) 20.dp else 40.dp)) + MyFeedCard( + feedItem = feed, + onLikeClick = { + val updated = feed.copy( + isLiked = !feed.isLiked, + likeCount = if (feed.isLiked) feed.likeCount - 1 else feed.likeCount + 1 + ) + feedStateList[index] = updated + }, + onContentClick = {} //TODO FeedCommentScreen으로 + ) + Spacer(modifier = Modifier.height(40.dp)) + if (index != feedStateList.lastIndex) { + HorizontalDivider( + color = colors.DarkGrey02, + thickness = 10.dp + ) + } + } + } + } + } + } +} + +@Preview +@Composable +private fun FeedOthersScreenPrev() { + ThipTheme { + val mockFeeds = List(5) { + FeedItem( + id = it + 1, + userProfileImage = R.drawable.character_literature, + userName = "user.$it", + userRole = "문학 칭호", + bookTitle = "책 제목 ", + authName = "한강", + timeAgo = "1시간 전", + content = "내용내용내용 입니다. 내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.내용내용내용 입니다.", + likeCount = it, + commentCount = it, + isLiked = false, + isSaved = false, + isLocked = it % 2 == 0, + imageUrls = listOf(R.drawable.bookcover_sample) + ) + } + val mockFollowerImages = listOf( + "https://example.com/image1.jpg", + "https://example.com/image2.jpg", + "https://example.com/image3.jpg", + "https://example.com/image4.jpg", + "https://example.com/image5.jpg" + ) + ThipTheme { + FeedOthersScreen( + nickname = "ThipUser01", + userRole = "문학 칭호", + feeds = mockFeeds, + totalFeedCount = mockFeeds.size, + followerProfileImageUrls = mockFollowerImages + ) + } + } +} + +@Preview +@Composable +private fun FeedOthersScreenWithoutFeedPrev() { + ThipTheme { + val mockFeeds: List = emptyList() + val mockFollowerImages = emptyList() + + ThipTheme { + FeedOthersScreen( + nickname = "ThipUser01", + userRole = "문학 칭호", + feeds = mockFeeds, + totalFeedCount = mockFeeds.size, + followerProfileImageUrls = mockFollowerImages + ) + } + } +} diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt index 98a4175a..10831a8c 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt @@ -121,8 +121,7 @@ fun FeedScreen( selectedTabIndex = selectedIndex.value, onTabSelected = { selectedIndex.value = it } ) - - // 스크롤 영역 전체 + // 스크롤 영역 LazyColumn( modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(12.dp) @@ -167,13 +166,13 @@ fun FeedScreen( Box( modifier = Modifier .fillMaxWidth() - .padding(top = 110.dp), + .padding(top = 244.dp), contentAlignment = Alignment.TopCenter ) { Text( text = stringResource(R.string.create_feed), style = typography.smalltitle_sb600_s18_h24, - color = colors.Grey + color = colors.White ) } } @@ -189,7 +188,7 @@ fun FeedScreen( ) feedStateList[index] = updated }, - onContentClick = {} //FeedCommentScreen으로 + onContentClick = {} //TODO FeedCommentScreen으로 ) Spacer(modifier = Modifier.height(40.dp)) if (index != feeds.lastIndex) { @@ -277,12 +276,31 @@ private fun FeedScreenPreview() { "https://example.com/image4.jpg", "https://example.com/image5.jpg" ) + ThipTheme { + FeedScreen( + nickname = "ThipUser01", + userRole = "문학 칭호", + selectedTabIndex = 1, + feeds = mockFeeds, + totalFeedCount = mockFeeds.size, + followerProfileImageUrls = mockFollowerImages + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun FeedScreenWithoutDataPreview() { + ThipTheme { + val mockFeeds: List = emptyList() + val mockFollowerImages = emptyList() ThipTheme { FeedScreen( nickname = "ThipUser01", userRole = "문학 칭호", - selectedTabIndex = 0, + selectedTabIndex = 1, feeds = mockFeeds, totalFeedCount = mockFeeds.size, followerProfileImageUrls = mockFollowerImages diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/SearchPeopleScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/SearchPeopleScreen.kt new file mode 100644 index 00000000..17a011f3 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/SearchPeopleScreen.kt @@ -0,0 +1,195 @@ +package com.texthip.thip.ui.feed.screen + +import androidx.compose.foundation.background +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +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.forms.SearchBookTextField +import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar +import com.texthip.thip.ui.feed.component.PeopleRecentSearch +import com.texthip.thip.ui.feed.component.SearchPeopleEmptyResult +import com.texthip.thip.ui.feed.component.SearchPeopleResult +import com.texthip.thip.ui.feed.mock.MySubscriptionData +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 SearchPeopleScreen( + modifier: Modifier = Modifier, + allPeople: List +) { + var recentSearches by rememberSaveable { + mutableStateOf(listOf("메롱", "메메롱", "메메메롱", "메메메", "메메루메루메루")) + } + var searchText by rememberSaveable { mutableStateOf("") } + var isSearched by rememberSaveable { mutableStateOf(false) } + val focusRequester = remember { FocusRequester() } + val focusManager = LocalFocusManager.current + + val liveSearchResults by remember(searchText, allPeople) { + derivedStateOf { + if (searchText.isBlank()) emptyList() + else allPeople.filter { person -> + person.nickname.contains(searchText, ignoreCase = true) + } + } + } + + val finalSearchResults by remember(searchText, isSearched, allPeople) { + derivedStateOf { + if (isSearched) { + if (searchText.isNotBlank()) { + allPeople.filter { person -> + person.nickname.contains(searchText, ignoreCase = true) + } + } else { + emptyList() + } + } else { + emptyList() + } + } + } + + LaunchedEffect(isSearched) { + if (isSearched) { + focusManager.clearFocus() + } + } + + Box( + modifier = modifier.fillMaxSize() + ) { + Column( + modifier = Modifier.fillMaxSize() + ) { + DefaultTopAppBar( + title = stringResource(R.string.search_user), + onLeftClick = {}, + ) + Column( + modifier = Modifier + .fillMaxSize() + ) { + Spacer(modifier = Modifier.height(16.dp)) + + SearchBookTextField( + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester) + .padding(horizontal = 20.dp), + hint = stringResource(R.string.search_user_you_look_for), + text = searchText, + onValueChange = { + searchText = it + isSearched = false + }, + onSearch = { query -> + if (query.isNotBlank() && !recentSearches.contains(query)) { + recentSearches = (listOf(query) + recentSearches).take(10) + } + isSearched = true + } + ) + Spacer(modifier = Modifier.height(16.dp)) + + when { + isSearched && finalSearchResults.isNotEmpty() -> { //검색했는데 결과 있음 + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(R.string.group_searched_room_size, finalSearchResults.size), + color = colors.Grey, + style = typography.menu_m500_s14_h24 + ) + } + Spacer( + modifier = Modifier + .padding(top = 4.dp, bottom = 16.dp) + .padding(horizontal = 20.dp) + .fillMaxWidth() + .height(1.dp) + .background(colors.DarkGrey02) + ) + SearchPeopleResult( + modifier = Modifier.weight(1f), + peopleList = finalSearchResults, + onThipNumClick = { person -> /*프로필로 이동*/ } + ) + } + isSearched && finalSearchResults.isEmpty() -> { //검색했는데 결과 없음 + SearchPeopleEmptyResult( + modifier = Modifier.padding(horizontal = 20.dp), + mainText = stringResource(R.string.no_user_you_look_for) + ) + } + searchText.isNotBlank() && !isSearched -> { //검색중 + SearchPeopleResult( + modifier = Modifier.weight(1f), + peopleList = liveSearchResults, + onThipNumClick = { person -> /* 프로필 화면으로 이동 */ } + ) + } + searchText.isBlank() && !isSearched -> { //최근검색어 보여주기 + PeopleRecentSearch( + modifier = Modifier.padding(horizontal = 20.dp), + recentSearches = recentSearches, + onSearchClick = { keyword -> + searchText = keyword + isSearched = true + }, + onRemove = { keyword -> + recentSearches = recentSearches.filterNot { it == keyword } + } + ) + } + } + } + } + } +} + + +@Preview +@Composable +fun PreviewGroupSearchScreen() { + ThipTheme { + SearchPeopleScreen( + allPeople = listOf( + MySubscriptionData(null, "메롱이", "인플루언서", colors.NeonGreen, 12, false), + MySubscriptionData(null, "메메롱이", "칭호", colors.NeonGreen, 1, false), + MySubscriptionData(null, "thip", "칭호칭호", colors.NeonGreen, 11, false), + MySubscriptionData(null, "Thip", "인플루언서", colors.NeonGreen, 111, false), + MySubscriptionData(null, "thip01", "작가", colors.NeonGreen, 0, false) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/component/BookContent.kt b/app/src/main/java/com/texthip/thip/ui/mypage/component/BookContent.kt new file mode 100644 index 00000000..51bcda64 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/mypage/component/BookContent.kt @@ -0,0 +1,70 @@ +package com.texthip.thip.ui.mypage.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.texthip.thip.R +import com.texthip.thip.ui.common.cards.CardBookList +import com.texthip.thip.ui.mypage.mock.BookItem +import com.texthip.thip.ui.mypage.mock.FeedItem +import com.texthip.thip.ui.mypage.viewmodel.SavedBookViewModel +import com.texthip.thip.ui.mypage.viewmodel.SavedFeedViewModel +import com.texthip.thip.ui.theme.ThipTheme.colors +import com.texthip.thip.ui.theme.ThipTheme.typography + +@Composable +fun BookContent( + bookList: List, viewModel: SavedBookViewModel +) { + //val books by viewModel.bookList.collectAsState() + + if (bookList.isEmpty()) { + EmptyBookContent() + } else { + LazyColumn { + items(bookList, key = { it.id }) { book -> + CardBookList( + title = book.title, + author = book.author, + imageRes = null, + publisher = book.publisher, + isBookmarked = book.isSaved, + onBookmarkClick = { viewModel.toggleBookmark(book.id) } + ) + } + } + } +} + +@Composable +fun EmptyBookContent() { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = stringResource(R.string.no_saved_book), + style = typography.smalltitle_sb600_s18_h24, + color = colors.White + ) + Text( + text = stringResource(R.string.do_thip_book), + style = typography.feedcopy_r400_s14_h20, + color = colors.Grey, + modifier = Modifier.padding(top = 8.dp) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/component/FeedContent.kt b/app/src/main/java/com/texthip/thip/ui/mypage/component/FeedContent.kt new file mode 100644 index 00000000..078eba10 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/mypage/component/FeedContent.kt @@ -0,0 +1,68 @@ +package com.texthip.thip.ui.mypage.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.texthip.thip.R +import com.texthip.thip.ui.mypage.mock.FeedItem +import com.texthip.thip.ui.mypage.viewmodel.SavedFeedViewModel +import com.texthip.thip.ui.theme.ThipTheme.colors +import com.texthip.thip.ui.theme.ThipTheme.typography + +@Composable +fun FeedContent( + feedList: List, viewModel: SavedFeedViewModel + +) { + if (feedList.isEmpty()) { + EmptyFeedContent() + } else { + LazyColumn { + items(feedList, key = { it.id }) { feed -> + val profileImagePainter = feed.userProfileImage?.let { painterResource(it) } + + SavedFeedCard( + feedItem = feed, + profileImage = profileImagePainter, + onBookmarkClick = { viewModel.toggleBookmark(feed.id) }, + onLikeClick = { viewModel.toggleLike(feed.id) } + ) + } + } + } +} + +@Composable +fun EmptyFeedContent() { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = stringResource(R.string.no_saved_feed), + style = typography.smalltitle_sb600_s18_h24, + color = colors.White + ) + Text( + text = stringResource(R.string.do_thip_feed), + style = typography.feedcopy_r400_s14_h20, + color = colors.Grey, + modifier = Modifier.padding(top = 8.dp) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/component/MypageSaveBook.kt b/app/src/main/java/com/texthip/thip/ui/mypage/component/MypageSaveBook.kt index f3cdd68f..e69de29b 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/component/MypageSaveBook.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/component/MypageSaveBook.kt @@ -1,28 +0,0 @@ -package com.texthip.thip.ui.mypage.component - -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.lifecycle.viewmodel.compose.viewModel -import com.texthip.thip.ui.common.cards.CardBookList -import com.texthip.thip.ui.mypage.viewmodel.SavedBookViewModel - -@Composable -fun BookContent(viewModel: SavedBookViewModel = viewModel()) { - val books by viewModel.bookList.collectAsState() - - LazyColumn { - items(items = books, key = { it.id }) { book -> - CardBookList( - title = book.title, - author = book.author, - imageUrl = null, - publisher = book.publisher, - isBookmarked = book.isSaved, - onBookmarkClick = { viewModel.toggleBookmark(book.id) } - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/component/MypageSaveFeed.kt b/app/src/main/java/com/texthip/thip/ui/mypage/component/MypageSaveFeed.kt deleted file mode 100644 index 970a7539..00000000 --- a/app/src/main/java/com/texthip/thip/ui/mypage/component/MypageSaveFeed.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.texthip.thip.ui.mypage.component - -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.res.painterResource -import androidx.lifecycle.viewmodel.compose.viewModel -import com.texthip.thip.ui.mypage.viewmodel.SavedFeedViewModel - -@Composable -fun FeedContent(viewModel: SavedFeedViewModel = viewModel()) { - val feedList by viewModel.feeds.collectAsState() - - LazyColumn { - items(feedList, key = { it.id }) { feed -> - val profileImagePainter = feed.userProfileImage?.let { painterResource(it) } - - SavedFeedCard( - feedItem = feed, - profileImage = profileImagePainter, - onBookmarkClick = { viewModel.toggleBookmark(feed.id) }, - onLikeClick = { viewModel.toggleLike(feed.id) } - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageReactionScreen.kt b/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageReactionScreen.kt index 70c2324b..d4b33d78 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageReactionScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageReactionScreen.kt @@ -11,11 +11,13 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -25,9 +27,11 @@ import com.texthip.thip.ui.common.buttons.OptionChipButton import com.texthip.thip.ui.common.cards.CardAlarm import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar import com.texthip.thip.ui.mypage.mock.ReactionItem +import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors +import com.texthip.thip.ui.theme.ThipTheme.typography -@Composable +/*@Composable fun ReactionsScreen() { //추후 뷰모델로 수정 예정 val reactions = listOf( @@ -107,10 +111,124 @@ fun ReactionsScreen() { } } } +}*/ +@Composable +fun ReactionsScreen( + reactions : List +) { + var isLikesSelected by remember { mutableStateOf(false) } + var isCommentsSelected by remember { mutableStateOf(false) } + + val filteredReactions = remember(isLikesSelected, isCommentsSelected, reactions) { + val showAll = (isLikesSelected && isCommentsSelected) || (!isLikesSelected && !isCommentsSelected) + if (showAll) { + reactions + } else { + reactions.filter { item -> + if (isLikesSelected) item.type == "좋아요" else item.type == "댓글" + } + } + } + + Column( + Modifier + .background(colors.Black) + .fillMaxSize() + ) { + DefaultTopAppBar( + title = stringResource(R.string.reactions), + onLeftClick = {}, + ) + Column( + modifier = Modifier + .fillMaxSize() + ) { + Row(modifier = Modifier.padding(start = 20.dp, top = 20.dp, bottom = 20.dp)) { + OptionChipButton( + text = stringResource(R.string.likes), + isFilled = true, + onClick = { isLikesSelected = !isLikesSelected } + ) + Spacer(modifier = Modifier.width(12.dp)) + OptionChipButton( + text = stringResource(R.string.comments), + isFilled = true, + onClick = { isCommentsSelected = !isCommentsSelected } + ) + } + if (filteredReactions.isEmpty()) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = stringResource(R.string.no_saved_reaction), + style = typography.smalltitle_sb600_s18_h24, + color = colors.White + ) + Text( + text = stringResource(R.string.save_first_reaction), + style = typography.feedcopy_r400_s14_h20, + color = colors.Grey, + modifier = Modifier.padding(top = 8.dp) + ) + } + } else { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 20.dp), + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + itemsIndexed (filteredReactions) { index, item -> + val isLastItem = index == filteredReactions.lastIndex + CardAlarm( + title = item.title, + message = item.message, + timeAgo = item.timeAgo, + isRead = true, + badgeText = item.type, + onClick = {}, + modifier = if (isLastItem) { + Modifier.padding(bottom = 20.dp) + } else { + Modifier + } + ) + } + } + } + } + } } @Preview @Composable private fun ReactionsScreenPrev() { - ReactionsScreen() -} \ No newline at end of file + val mockReactions = listOf( + ReactionItem("@user 1님의 피드 글","어쩌구 저쩌구 콘텐츠 내용입니다.","좋아요", "2"), + ReactionItem("@user 1님의 글", "어쩌구 저쩌구 콘텐츠 내용입니다.","댓글","7"), + ReactionItem("@user 3님의 기록", "어쩌구 저쩌구 콘텐츠 내용입니다.","댓글", "17"), + ReactionItem("@user 1님의 글", "어쩌구 저쩌구 콘텐츠 내용입니다.", "좋아요", "25"), + ReactionItem("@user 1님의 피드 글","어쩌구 저쩌구 콘텐츠 내용입니다.","좋아요", "2"), + ReactionItem("@user 1님의 글", "어쩌구 저쩌구 콘텐츠 내용입니다.","댓글","7"), + ReactionItem("@user 3님의 기록", "어쩌구 저쩌구 콘텐츠 내용입니다.","댓글", "17"), + ReactionItem("@user 1님의 글", "어쩌구 저쩌구 콘텐츠 내용입니다.", "좋아요", "25"), + ReactionItem("@user 1님의 피드 글","어쩌구 저쩌구 콘텐츠 내용입니다.","좋아요", "2"), + ReactionItem("@user 1님의 글", "어쩌구 저쩌구 콘텐츠 내용입니다.","댓글","7"), + ReactionItem("@user 3님의 기록", "어쩌구 저쩌구 콘텐츠 내용입니다.","댓글", "17"), + ReactionItem("@user 1님의 글", "어쩌구 저쩌구 콘텐츠 내용입니다.", "좋아요", "25") + ) + ThipTheme { + ReactionsScreen(reactions = mockReactions) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFF000000) +@Composable +private fun ReactionsScreenEmptyPrev() { + ThipTheme { + ReactionsScreen(reactions = emptyList()) + } +} diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageSaveScreen.kt b/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageSaveScreen.kt index e57cc60c..25e498f2 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageSaveScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageSaveScreen.kt @@ -1,5 +1,6 @@ package com.texthip.thip.ui.mypage.screen +import android.annotation.SuppressLint import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -14,6 +15,7 @@ import androidx.compose.material3.TabRow import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable @@ -26,18 +28,31 @@ 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 androidx.lifecycle.viewmodel.compose.viewModel import com.texthip.thip.R import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar import com.texthip.thip.ui.mypage.component.BookContent +import com.texthip.thip.ui.mypage.component.EmptyBookContent import com.texthip.thip.ui.mypage.component.FeedContent +import com.texthip.thip.ui.mypage.viewmodel.EmptySavedBookViewModel +import com.texthip.thip.ui.mypage.viewmodel.EmptySavedFeedViewModel +import com.texthip.thip.ui.mypage.viewmodel.SavedBookViewModel +import com.texthip.thip.ui.mypage.viewmodel.SavedFeedViewModel +import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography import com.texthip.thip.ui.theme.White @Composable -fun SavedScreen() { +fun SavedScreen( + feedViewModel: SavedFeedViewModel = viewModel(), + bookViewModel: SavedBookViewModel = viewModel() + +) { val tabs = listOf(stringResource(R.string.feed), stringResource(R.string.book)) var selectedTabIndex by rememberSaveable { mutableStateOf(0) } + val feedList by feedViewModel.feeds.collectAsState() + val bookList by bookViewModel.books.collectAsState() Column( Modifier @@ -52,7 +67,9 @@ fun SavedScreen() { modifier = Modifier .fillMaxSize() ) { - Box(modifier = Modifier.width(160.dp).padding(start = 20.dp)) { + Box(modifier = Modifier + .width(160.dp) + .padding(start = 20.dp)) { TabRow( selectedTabIndex = selectedTabIndex, containerColor = Color.Transparent, @@ -93,8 +110,8 @@ fun SavedScreen() { } Box(modifier = Modifier.fillMaxWidth()) { when (selectedTabIndex) { - 0 -> FeedContent() - 1 -> BookContent() + 0 -> FeedContent(feedList = feedList, viewModel = feedViewModel) + 1 -> BookContent(bookList = bookList, viewModel = bookViewModel) } } } @@ -102,8 +119,24 @@ fun SavedScreen() { } +@SuppressLint("ViewModelConstructorInComposable") @Preview @Composable private fun SavedScreenPrev() { - SavedScreen() + SavedScreen( + feedViewModel = SavedFeedViewModel(), + bookViewModel = SavedBookViewModel() + ) +} + +@SuppressLint("ViewModelConstructorInComposable") +@Preview +@Composable +private fun SavedScreenWithoutFeedPrev() { + ThipTheme { + SavedScreen( + feedViewModel = EmptySavedFeedViewModel(), + bookViewModel = EmptySavedBookViewModel() + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedBookViewModel.kt b/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedBookViewModel.kt index a0ff0759..fec20fc2 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedBookViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedBookViewModel.kt @@ -2,20 +2,21 @@ package com.texthip.thip.ui.mypage.viewmodel import androidx.lifecycle.ViewModel import com.texthip.thip.ui.mypage.mock.BookItem +import com.texthip.thip.ui.mypage.mock.FeedItem import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -class SavedBookViewModel : ViewModel() { +open class SavedBookViewModel : ViewModel() { - private val _bookList = MutableStateFlow>(emptyList()) - val bookList: StateFlow> = _bookList + protected val _books = MutableStateFlow>(emptyList()) + open val books: StateFlow> = _books init { loadMockBooks() } - private fun loadMockBooks() { - _bookList.value = listOf( + open fun loadMockBooks() { + _books.value = listOf( BookItem( id = 1, title = "이기적 유전자", @@ -82,10 +83,14 @@ class SavedBookViewModel : ViewModel() { ) ) } - fun toggleBookmark(id: Int) { - _bookList.value = _bookList.value.map { + _books.value = _books.value.map { if (it.id == id) it.copy(isSaved = !it.isSaved) else it } } +} + +class EmptySavedBookViewModel : SavedBookViewModel() { + override fun loadMockBooks() {} + } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt b/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt index d68983d8..056d05fe 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt @@ -6,7 +6,7 @@ import com.texthip.thip.ui.mypage.mock.FeedItem import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -class SavedFeedViewModel: ViewModel() { +open class SavedFeedViewModel: ViewModel() { private val _feeds = MutableStateFlow( listOf( FeedItem( @@ -86,7 +86,9 @@ class SavedFeedViewModel: ViewModel() { ) ) - val feeds: StateFlow> = _feeds + + + open val feeds: StateFlow> = _feeds fun toggleBookmark(id: Int) { _feeds.value = _feeds.value.map { @@ -99,4 +101,12 @@ class SavedFeedViewModel: ViewModel() { if (it.id == id) it.copy(isLiked = !it.isLiked) else it } } +} + +class EmptySavedFeedViewModel : SavedFeedViewModel() { + + private val _feeds = MutableStateFlow>(emptyList()) + + override val feeds: StateFlow> = _feeds + } \ No newline at end of file diff --git a/app/src/main/res/drawable/search_character_image.png b/app/src/main/res/drawable/search_character_image.png new file mode 100644 index 00000000..0e8a899c Binary files /dev/null and b/app/src/main/res/drawable/search_character_image.png differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d0b7aa4b..dade4eff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -125,6 +125,13 @@ texthip2025@gmail.com 이메일로 닉네임과 문의사항을 보내주시면\n빠른 시일 내로 해결해 드릴게요! + 아직 저장한 책이 없어요 + 아직 저장한 피드가 없어요 + 마음에 드는 책을 THIP 해보세요! + 마음에 드는 피드를 THIP 해보세요! + + 아직 저장한 반응이 없어요 + 첫 번째 반응을 저장해 보세요! 진행중 @@ -293,8 +300,11 @@ %1$s 님을 띱 취소했어요 띱 하기 띱 취소 - %d명이 띱 하는 중 - 피드에 글을 작성해 보세요 + %1$d명 + 이 띱 하는 중 + 피드에 글을 작성해보세요 + 피드에 작성된 글이 없어요 + 관심있는 독서메이트를 찾아보세요! 모집을 마감하시겠습니까? 독서메이트 모집을 마감하면\n지금 바로 모임방 활동을 바로 시작할 수 있어요. @@ -322,6 +332,10 @@ 삭제하기 이 피드를 삭제하시겠어요? 삭제 후에는 되돌릴 수 없어요. + 찾는 사용자가 없어요 + 사용자 찾기 + 내가 찾는 사용자를 검색해보세요. + 가장 많이 검색된 책