From 60c862ad02d3aabaeb17bec3012f08e0f4b06335 Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Fri, 19 Dec 2025 23:29:26 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat(home):=20HomeHeader=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EC=A0=80=EB=B8=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/home/component/HomeHeader.kt | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt new file mode 100644 index 0000000..8a564ae --- /dev/null +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt @@ -0,0 +1,63 @@ +package com.daedan.festabook.presentation.home.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.daedan.festabook.R +import com.daedan.festabook.presentation.theme.FestabookColor +import com.daedan.festabook.presentation.theme.FestabookTypography + +@Composable +fun HomeHeader( + schoolName: String, + onExpandClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Box( + modifier = + modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 12.dp), + ) { + Row( + modifier = Modifier.clickable { onExpandClick() }, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = schoolName, + style = FestabookTypography.displayLarge, + color = FestabookColor.black, + ) + + Spacer(modifier = Modifier.width(4.dp)) + + Image( + painter = painterResource(id = R.drawable.ic_dropdown), + contentDescription = stringResource(id = R.string.home_navigate_to_explore_desc), + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun HomeHeaderPreview() { + HomeHeader( + schoolName = "가천대학교", + onExpandClick = {}, + ) +} From 3af132ee53b99e0647c256ce88e839abdcfef32f Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Fri, 19 Dec 2025 23:42:14 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat(Home):=20HomeFestivalInfo=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EC=A0=80=EB=B8=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/component/HomeFestivalInfo.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 app/src/main/java/com/daedan/festabook/presentation/home/component/HomeFestivalInfo.kt diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeFestivalInfo.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeFestivalInfo.kt new file mode 100644 index 0000000..7d85261 --- /dev/null +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeFestivalInfo.kt @@ -0,0 +1,52 @@ +package com.daedan.festabook.presentation.home.component + +import androidx.compose.foundation.layout.Column +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.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.daedan.festabook.presentation.theme.FestabookColor +import com.daedan.festabook.presentation.theme.FestabookTypography + +@Composable +fun HomeFestivalInfo( + festivalName: String, + festivalDate: String, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.fillMaxWidth(), + ) { + Text( + text = festivalName, + style = FestabookTypography.displayMedium, + color = FestabookColor.black, + modifier = Modifier.padding(horizontal = 20.dp), + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = festivalDate, + style = FestabookTypography.bodyLarge, + color = FestabookColor.gray500, + modifier = Modifier.padding(horizontal = 20.dp), + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun HomeFestivalInfoPreview() { + HomeFestivalInfo( + festivalName = "2025 가천 Water Festival\n: AQUA WAVE", + festivalDate = "2025년 10월 15일 - 10월 17일", + ) +} From bb90581e4fbe0cab2e797c90bea894c8ea3e5dd7 Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Fri, 19 Dec 2025 23:51:11 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat(home):=20HomeLineupHeader=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EC=A0=80=EB=B8=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/component/HomeLineupHeader.kt | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt new file mode 100644 index 0000000..e0a9471 --- /dev/null +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt @@ -0,0 +1,78 @@ +package com.daedan.festabook.presentation.home.component + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +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.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.daedan.festabook.R +import com.daedan.festabook.presentation.theme.FestabookColor +import com.daedan.festabook.presentation.theme.FestabookTypography + +@Composable +fun HomeLineupHeader( + onScheduleClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = + modifier + .fillMaxWidth() + .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 16.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource(R.string.home_lineup_title), + style = FestabookTypography.displayMedium, + color = FestabookColor.black, + ) + + Row( + modifier = + Modifier + .clickable( + onClick = onScheduleClick, + ) + .padding(4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource(R.string.home_check_schedule_text), + style = FestabookTypography.bodySmall, + color = FestabookColor.gray400, + ) + + Spacer(modifier = Modifier.width(4.dp)) + + Icon( + painter = painterResource(id = R.drawable.ic_arrow_forward_right), + contentDescription = null, + tint = FestabookColor.gray400, + modifier = Modifier.size(12.dp), + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun HomeLineupHeaderPreview() { + HomeLineupHeader( + onScheduleClick = {}, + ) +} From 19e9842cd0e646c214080dcd15ca835df70ecb59 Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Sat, 20 Dec 2025 00:23:00 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat(home):=20HomePosterList=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EC=A0=80=EB=B8=94=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20?= =?UTF-8?q?=EB=AC=B4=ED=95=9C=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A0=80=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/component/HomePosterList.kt | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 app/src/main/java/com/daedan/festabook/presentation/home/component/HomePosterList.kt diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomePosterList.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomePosterList.kt new file mode 100644 index 0000000..1ce0af7 --- /dev/null +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomePosterList.kt @@ -0,0 +1,112 @@ +package com.daedan.festabook.presentation.home.component + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.PageSize +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.lerp +import com.daedan.festabook.presentation.common.component.CoilImage +import kotlin.math.absoluteValue + +@Composable +fun HomePosterList( + posterUrls: List, + modifier: Modifier = Modifier, +) { + if (posterUrls.isEmpty()) return + + // 무한 스크롤을 위한 큰 수 설정 + val initialPage = (Int.MAX_VALUE / 2) - ((Int.MAX_VALUE / 2) % posterUrls.size) + val pagerState = + rememberPagerState( + initialPage = initialPage, + pageCount = { Int.MAX_VALUE }, + ) + + val configuration = LocalConfiguration.current + val screenWidth = configuration.screenWidthDp.dp + val itemWidth = 300.dp + // 화면 중앙에 아이템이 오도록 패딩 계산 + val horizontalPadding = (screenWidth - itemWidth) / 2 + + HorizontalPager( + state = pagerState, + pageSize = PageSize.Fixed(itemWidth), + contentPadding = PaddingValues(horizontal = horizontalPadding), + pageSpacing = 12.dp, + modifier = + modifier + .fillMaxWidth() + .height(400.dp), // item_home_poster 높이 + verticalAlignment = Alignment.CenterVertically, + ) { page -> + val actualIndex = page % posterUrls.size + val imageUrl = posterUrls[actualIndex] + + // 스크롤 위치에 따른 Scale 계산 + val pageOffset = + ((pagerState.currentPage - page) + pagerState.currentPageOffsetFraction).absoluteValue + + // 중앙(0)이면 1.0f, 멀어질수록 작아짐 (최소 0.9f) + val scale = + lerp( + start = 1.0f, + stop = 0.9f, + fraction = pageOffset.coerceIn(0f, 1f), + ) + + // 투명도 조절 (중앙은 1.0, 멀어지면 약간 투명하게) + val alpha = + lerp( + start = 1.0f, + stop = 0.6f, + fraction = pageOffset.coerceIn(0f, 1f), + ) + + Box( + modifier = + Modifier + .width(itemWidth) + .height(400.dp) + .graphicsLayer { + scaleX = scale + scaleY = scale + this.alpha = alpha + } + .clip(RoundedCornerShape(10.dp)), + ) { + CoilImage( + url = imageUrl, + contentDescription = null, + modifier = Modifier.fillMaxSize(), + ) + } + } +} + +@Preview +@Composable +private fun HomePosterListPreview() { + HomePosterList( + posterUrls = + listOf( + "sample", + "sample", + "sample", + ), + ) +} \ No newline at end of file From c73796eef6ce716b783f6b3fe6d6ea6d8ff02335 Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Mon, 22 Dec 2025 22:46:56 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat(home):=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=95=84=ED=8B=B0=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=EC=97=85=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EC=A0=80=EB=B8=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/component/HomeArtistItem.kt | 70 ++++++++ .../home/component/HomeLineupItem.kt | 150 ++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 app/src/main/java/com/daedan/festabook/presentation/home/component/HomeArtistItem.kt create mode 100644 app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupItem.kt diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeArtistItem.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeArtistItem.kt new file mode 100644 index 0000000..478baa8 --- /dev/null +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeArtistItem.kt @@ -0,0 +1,70 @@ +package com.daedan.festabook.presentation.home.component + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.daedan.festabook.presentation.common.component.CoilImage +import com.daedan.festabook.presentation.theme.FestabookColor +import com.daedan.festabook.presentation.theme.FestabookTypography + +@Composable +fun HomeArtistItem( + artistName: String, + artistImageUrl: String, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.width(68.dp), + ) { + CoilImage( + url = artistImageUrl, + contentDescription = null, + modifier = + Modifier + .fillMaxWidth() + .aspectRatio(1f) + .clip(HomeArtistItem.ArtistImage) + .border(1.dp, FestabookColor.gray300, HomeArtistItem.ArtistImage), + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = artistName, + style = FestabookTypography.labelLarge, + color = FestabookColor.gray700, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } +} + +object HomeArtistItem { + val ArtistImage = RoundedCornerShape( + topStartPercent = 50, + topEndPercent = 50, + bottomEndPercent = 50, + bottomStartPercent = 5, + ) +} + +@Preview +@Composable +private fun HomeArtistItemPreview() { + HomeArtistItem( + artistName = "실리카겔", + artistImageUrl = "sample", + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupItem.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupItem.kt new file mode 100644 index 0000000..af9efc0 --- /dev/null +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupItem.kt @@ -0,0 +1,150 @@ +package com.daedan.festabook.presentation.home.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.PaddingValues +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.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.daedan.festabook.R +import com.daedan.festabook.presentation.home.LineUpItemOfDayUiModel +import com.daedan.festabook.presentation.home.LineupItemUiModel +import com.daedan.festabook.presentation.theme.FestabookColor +import com.daedan.festabook.presentation.theme.FestabookTypography +import java.time.LocalDate +import java.time.LocalDateTime + +@Composable +fun HomeLineupItem( + uiModel: LineUpItemOfDayUiModel, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.fillMaxWidth(), + ) { + // 날짜 + 배지 영역 + Row( + modifier = Modifier.padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = "${uiModel.date.monthValue}.${uiModel.date.dayOfMonth}", + style = FestabookTypography.titleLarge, + color = FestabookColor.black, + ) + + if (uiModel.isDDay) { + Spacer(modifier = Modifier.width(6.dp)) + Box( + modifier = + Modifier + .clip(RoundedCornerShape(20.dp)) + .background(FestabookColor.black) + .padding(horizontal = 6.dp, vertical = 2.dp), + ) { + Text( + text = stringResource(id = R.string.home_is_d_day), + style = FestabookTypography.labelSmall, + color = FestabookColor.white, + ) + } + } + } + + Spacer(modifier = Modifier.height(4.dp)) + + Box( + modifier = + Modifier + .padding(horizontal = 16.dp) + .width(75.dp) + .height(1.dp) + .background(FestabookColor.gray700), + ) + + Spacer(modifier = Modifier.height(8.dp)) + + // 아티스트 가로 리스트 + LazyRow( + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + items(uiModel.lineupItems) { item -> + HomeArtistItem( + artistName = item.name, + artistImageUrl = item.imageUrl, + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + } +} + +@Preview(showBackground = true) +@Composable +private fun HomeLineupItemPreview() { + HomeLineupItem( + uiModel = + LineUpItemOfDayUiModel( + id = 1L, + date = LocalDate.now(), + isDDay = true, + lineupItems = + listOf( + LineupItemUiModel( + id = 1, + name = "실리카겔", + imageUrl = "sample", + performanceAt = LocalDateTime.now(), + ), + LineupItemUiModel( + id = 2, + name = "한로로", + imageUrl = "sample", + performanceAt = LocalDateTime.now(), + ), + LineupItemUiModel( + id = 3, + name = "실리카겔", + imageUrl = "sample", + performanceAt = LocalDateTime.now(), + ), + LineupItemUiModel( + id = 4, + name = "한로로", + imageUrl = "sample", + performanceAt = LocalDateTime.now(), + ), + LineupItemUiModel( + id = 5, + name = "실리카겔", + imageUrl = "sample", + performanceAt = LocalDateTime.now(), + ), + LineupItemUiModel( + id = 6, + name = "한로로", + imageUrl = "sample", + performanceAt = LocalDateTime.now(), + ), + ), + ), + ) +} From 9ef8418688be4c25a7a42360d9aee3612801914a Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Fri, 26 Dec 2025 17:40:17 +0900 Subject: [PATCH 06/11] =?UTF-8?q?feat(home):=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20Compose=20=EC=A0=84=ED=99=98=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/home/HomeFragment.kt | 175 ++-------------- .../presentation/home/HomeViewModel.kt | 25 ++- .../presentation/home/component/HomeHeader.kt | 9 +- .../presentation/home/component/HomeScreen.kt | 196 ++++++++++++++++++ .../presentation/main/MainActivity.kt | 16 +- .../presentation/setting/SettingFragment.kt | 38 ++-- 6 files changed, 269 insertions(+), 190 deletions(-) create mode 100644 app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/HomeFragment.kt b/app/src/main/java/com/daedan/festabook/presentation/home/HomeFragment.kt index 2b0b834..ed10622 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/HomeFragment.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/HomeFragment.kt @@ -1,184 +1,49 @@ package com.daedan.festabook.presentation.home import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.fragment.app.Fragment +import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.PagerSnapHelper import androidx.recyclerview.widget.RecyclerView import com.daedan.festabook.R import com.daedan.festabook.databinding.FragmentHomeBinding import com.daedan.festabook.di.fragment.FragmentKey -import com.daedan.festabook.logging.logger -import com.daedan.festabook.logging.model.home.ExploreClickLogData -import com.daedan.festabook.logging.model.home.HomeViewLogData -import com.daedan.festabook.logging.model.home.ScheduleClickLogData import com.daedan.festabook.presentation.common.BaseFragment -import com.daedan.festabook.presentation.common.formatFestivalPeriod -import com.daedan.festabook.presentation.common.showErrorSnackBar import com.daedan.festabook.presentation.explore.ExploreActivity -import com.daedan.festabook.presentation.home.adapter.CenterItemMotionEnlarger -import com.daedan.festabook.presentation.home.adapter.FestivalUiState -import com.daedan.festabook.presentation.home.adapter.LineUpItemOfDayAdapter -import com.daedan.festabook.presentation.home.adapter.PosterAdapter +import com.daedan.festabook.presentation.home.component.HomeScreen import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesIntoMap import dev.zacsweers.metro.Inject import dev.zacsweers.metro.binding -import timber.log.Timber -@ContributesIntoMap(scope = AppScope::class, binding = binding()) +@ContributesIntoMap(scope = AppScope::class, binding = binding()) @FragmentKey(HomeFragment::class) -class HomeFragment @Inject constructor( - private val centerItemMotionEnlarger: RecyclerView.OnScrollListener, -) : BaseFragment() { +class HomeFragment @Inject constructor() : BaseFragment() { override val layoutId: Int = R.layout.fragment_home @Inject override lateinit var defaultViewModelProviderFactory: ViewModelProvider.Factory private val viewModel: HomeViewModel by viewModels({ requireActivity() }) - private val posterAdapter: PosterAdapter by lazy { - PosterAdapter() - } - - private val lineupOfDayAdapter: LineUpItemOfDayAdapter by lazy { - LineUpItemOfDayAdapter() - } - - override fun onViewCreated( - view: View, + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle?, - ) { - super.onViewCreated(view, savedInstanceState) - binding.lifecycleOwner = viewLifecycleOwner - setupObservers() - setupAdapters() - setupNavigateToScheduleButton() - setupNavigateToExploreButton() - } - - private fun setupNavigateToExploreButton() { - binding.layoutTitleWithIcon.setOnClickListener { - binding.logger.log(ExploreClickLogData(binding.logger.getBaseLogData())) - - startActivity(ExploreActivity.newIntent(requireContext())) - } - } - - private fun setupNavigateToScheduleButton() { - binding.btnNavigateToSchedule.setOnClickListener { - binding.logger.log( - ScheduleClickLogData( - baseLogData = binding.logger.getBaseLogData(), - ), - ) - - viewModel.navigateToScheduleClick() - } - } - - private fun setupObservers() { - viewModel.festivalUiState.observe(viewLifecycleOwner) { festivalUiState -> - when (festivalUiState) { - is FestivalUiState.Loading -> {} - is FestivalUiState.Success -> handleSuccessState(festivalUiState) - is FestivalUiState.Error -> { - showErrorSnackBar(festivalUiState.throwable) - Timber.w( - festivalUiState.throwable, - "HomeFragment: ${festivalUiState.throwable.message}", - ) - } - } - } - viewModel.lineupUiState.observe(viewLifecycleOwner) { lineupUiState -> - when (lineupUiState) { - is LineupUiState.Loading -> {} - is LineupUiState.Success -> { - lineupOfDayAdapter.submitList(lineupUiState.lineups.getLineupItems()) - } - - is LineupUiState.Error -> { - showErrorSnackBar(lineupUiState.throwable) - Timber.w( - lineupUiState.throwable, - "HomeFragment: ${lineupUiState.throwable.message}", - ) - } - } - } - } - - private fun setupAdapters() { - binding.rvHomePoster.adapter = posterAdapter - binding.rvHomeLineup.adapter = lineupOfDayAdapter - attachSnapHelper() - addScrollEffectListener() - } - - private fun handleSuccessState(festivalUiState: FestivalUiState.Success) { - binding.tvHomeOrganizationTitle.text = - festivalUiState.organization.universityName - binding.tvHomeFestivalTitle.text = - festivalUiState.organization.festival.festivalName - binding.tvHomeFestivalDate.text = - formatFestivalPeriod( - festivalUiState.organization.festival.startDate, - festivalUiState.organization.festival.endDate, - ) - - val posterUrls = - festivalUiState.organization.festival.festivalImages - .sortedBy { it.sequence } - .map { it.imageUrl } - - if (posterUrls.isNotEmpty()) { - posterAdapter.submitList(posterUrls) { - scrollToInitialPosition(posterUrls.size) + ): View { + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + HomeScreen( + viewModel = viewModel, + onNavigateToExplore = { + startActivity(ExploreActivity.newIntent(requireContext())) + }, + ) } } - binding.logger.log( - HomeViewLogData( - baseLogData = binding.logger.getBaseLogData(), - universityName = festivalUiState.organization.universityName, - festivalId = festivalUiState.organization.id, - ), - ) - } - - private fun attachSnapHelper() { - PagerSnapHelper().attachToRecyclerView(binding.rvHomePoster) - } - - private fun scrollToInitialPosition(size: Int) { - val safeMaxValue = Int.MAX_VALUE / INFINITE_SCROLL_SAFETY_FACTOR - val initialPosition = safeMaxValue - (safeMaxValue % size) - - val layoutManager = binding.rvHomePoster.layoutManager as? LinearLayoutManager ?: return - - val itemWidth = resources.getDimensionPixelSize(R.dimen.poster_item_width) - val offset = (binding.rvHomePoster.width / 2) - (itemWidth / 2) - - layoutManager.scrollToPositionWithOffset(initialPosition, offset) - - binding.rvHomePoster.post { - (centerItemMotionEnlarger as CenterItemMotionEnlarger).expandCenterItem(binding.rvHomePoster) - } - } - - private fun addScrollEffectListener() { - binding.rvHomePoster.addOnScrollListener(centerItemMotionEnlarger) - } - - override fun onDestroyView() { - binding.rvHomePoster.clearOnScrollListeners() - super.onDestroyView() - } - - companion object { - private const val INFINITE_SCROLL_SAFETY_FACTOR = 4 } } diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/HomeViewModel.kt b/app/src/main/java/com/daedan/festabook/presentation/home/HomeViewModel.kt index 85fb947..64caaff 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/HomeViewModel.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/HomeViewModel.kt @@ -1,17 +1,19 @@ package com.daedan.festabook.presentation.home -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.daedan.festabook.di.viewmodel.ViewModelKey -import com.daedan.festabook.di.viewmodel.ViewModelScope import com.daedan.festabook.domain.repository.FestivalRepository -import com.daedan.festabook.presentation.common.SingleLiveData import com.daedan.festabook.presentation.home.adapter.FestivalUiState import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesIntoMap import dev.zacsweers.metro.Inject +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @ContributesIntoMap(AppScope::class) @@ -19,14 +21,15 @@ import kotlinx.coroutines.launch class HomeViewModel @Inject constructor( private val festivalRepository: FestivalRepository, ) : ViewModel() { - private val _festivalUiState = MutableLiveData() - val festivalUiState: LiveData get() = _festivalUiState + private val _festivalUiState = MutableStateFlow(FestivalUiState.Loading) + val festivalUiState: StateFlow = _festivalUiState.asStateFlow() - private val _lineupUiState = MutableLiveData() - val lineupUiState: LiveData get() = _lineupUiState + private val _lineupUiState = MutableStateFlow(LineupUiState.Loading) + val lineupUiState: StateFlow = _lineupUiState.asStateFlow() - private val _navigateToScheduleEvent: SingleLiveData = SingleLiveData() - val navigateToScheduleEvent: LiveData get() = _navigateToScheduleEvent + private val _navigateToScheduleEvent = + MutableSharedFlow(replay = 0, extraBufferCapacity = 1) + val navigateToScheduleEvent: SharedFlow = _navigateToScheduleEvent.asSharedFlow() init { loadFestival() @@ -48,7 +51,7 @@ class HomeViewModel @Inject constructor( } fun navigateToScheduleClick() { - _navigateToScheduleEvent.setValue(Unit) + _navigateToScheduleEvent.tryEmit(Unit) } private fun loadLineup() { diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt index 8a564ae..836e48c 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt @@ -15,8 +15,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.PlatformTextStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.daedan.festabook.R import com.daedan.festabook.presentation.theme.FestabookColor import com.daedan.festabook.presentation.theme.FestabookTypography @@ -31,7 +33,7 @@ fun HomeHeader( modifier = modifier .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 12.dp), + .padding(horizontal = 16.dp) ) { Row( modifier = Modifier.clickable { onExpandClick() }, @@ -39,7 +41,10 @@ fun HomeHeader( ) { Text( text = schoolName, - style = FestabookTypography.displayLarge, + style = FestabookTypography.displayLarge.copy( + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeight = 34.sp + ), color = FestabookColor.black, ) diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt new file mode 100644 index 0000000..e9e5aec --- /dev/null +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt @@ -0,0 +1,196 @@ +package com.daedan.festabook.presentation.home.component + +import androidx.compose.foundation.layout.Box +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.HorizontalDivider +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.daedan.festabook.presentation.common.formatFestivalPeriod +import com.daedan.festabook.presentation.home.HomeViewModel +import com.daedan.festabook.presentation.home.LineUpItemGroupUiModel +import com.daedan.festabook.presentation.home.LineupItemUiModel +import com.daedan.festabook.presentation.home.adapter.FestivalUiState +import com.daedan.festabook.domain.model.Festival +import com.daedan.festabook.domain.model.Organization +import com.daedan.festabook.domain.model.Poster +import com.daedan.festabook.presentation.home.LineupUiState +import com.daedan.festabook.presentation.theme.FestabookColor +import kotlinx.coroutines.flow.collectLatest +import java.time.LocalDate +import java.time.LocalDateTime + +@Composable +fun HomeScreen( + viewModel: HomeViewModel, + onNavigateToExplore: () -> Unit, + modifier: Modifier = Modifier, +) { + val festivalUiState by viewModel.festivalUiState.collectAsState() + val lineupUiState by viewModel.lineupUiState.collectAsState() + + FestivalOverview( + festivalUiState = festivalUiState, + lineupUiState = lineupUiState, + onNavigateToExplore = onNavigateToExplore, + onNavigateToSchedule = viewModel::navigateToScheduleClick, + modifier = modifier, + ) +} + +@Composable +fun FestivalOverview( + festivalUiState: FestivalUiState, + lineupUiState: LineupUiState, + onNavigateToExplore: () -> Unit, + onNavigateToSchedule: () -> Unit, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier.fillMaxSize(), + containerColor = Color.White, + ) { + LazyColumn( + modifier = + Modifier.fillMaxSize() + ) { + // 헤더 (학교 이름) + item { + if (festivalUiState is FestivalUiState.Success) { + HomeHeader( + schoolName = festivalUiState.organization.universityName, + onExpandClick = onNavigateToExplore, + modifier = Modifier.padding(top = 40.dp), + ) + } + } + + // 포스터 리스트 + item { + if (festivalUiState is FestivalUiState.Success) { + val posterUrls = + festivalUiState.organization.festival.festivalImages + .sortedBy { it.sequence } + .map { it.imageUrl } + + HomePosterList( + posterUrls = posterUrls, + modifier = Modifier.padding(vertical = 12.dp), + ) + } + } + + // 축제 정보 + item { + if (festivalUiState is FestivalUiState.Success) { + val festival = festivalUiState.organization.festival + HomeFestivalInfo( + festivalName = festival.festivalName, + festivalDate = + formatFestivalPeriod( + festival.startDate, + festival.endDate, + ), + modifier = Modifier.padding(top = 16.dp), + ) + } + } + + + // 구분선 + item { + if (festivalUiState is FestivalUiState.Success) { + HorizontalDivider( + thickness = 4.dp, + color = FestabookColor.gray200, + modifier = + Modifier + .padding(top = 16.dp), + ) + } + } + + // 라인업 헤더 + item { + HomeLineupHeader( + onScheduleClick = onNavigateToSchedule, + ) + } + + // 라인업 리스트 + when (lineupUiState) { + is LineupUiState.Success -> { + val lineups = lineupUiState.lineups.getLineupItems() + items(lineups) { lineupItem -> + HomeLineupItem(uiModel = lineupItem) + } + } + + is LineupUiState.Loading -> { + // 로딩 시 동작 논의 후 추가 + } + + is LineupUiState.Error -> { + // 에러 표시 + } + } + + // 하단 여백 추가 + item { + Box(modifier = Modifier.padding(bottom = 60.dp)) + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun FestivalOverviewPreview() { + val sampleFestival = + Organization( + id = 1, + universityName = "가천대학교", + festival = + Festival( + festivalName = "2025 가천 Water Festival\n: AQUA WAVE", + startDate = LocalDate.now(), + endDate = LocalDate.now().plusDays(2), + festivalImages = + listOf( + Poster(1, "sample", 1), + Poster(2, "sample", 2), + ), + ), + ) + + val sampleLineups = + LineUpItemGroupUiModel( + group = + mapOf( + LocalDate.now() to + listOf( + LineupItemUiModel(1, "sample", "실리카겔", LocalDateTime.now()), + LineupItemUiModel(2, "sample", "아이유", LocalDateTime.now()), + ), + LocalDate.now().plusDays(1) to + listOf( + LineupItemUiModel(3, "sample", "뉴진스", LocalDateTime.now()), + ), + ), + ) + + FestivalOverview( + festivalUiState = FestivalUiState.Success(sampleFestival), + lineupUiState = LineupUiState.Success(sampleLineups), + onNavigateToExplore = {}, + onNavigateToSchedule = {}, + ) +} diff --git a/app/src/main/java/com/daedan/festabook/presentation/main/MainActivity.kt b/app/src/main/java/com/daedan/festabook/presentation/main/MainActivity.kt index 8df33ef..17e2b8c 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/main/MainActivity.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/main/MainActivity.kt @@ -19,7 +19,10 @@ import androidx.fragment.app.FragmentFactory import androidx.fragment.app.add import androidx.fragment.app.commit import androidx.fragment.app.commitNow +import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.daedan.festabook.R import com.daedan.festabook.databinding.ActivityMainBinding import com.daedan.festabook.di.appGraph @@ -39,6 +42,8 @@ import com.daedan.festabook.presentation.setting.SettingFragment import com.daedan.festabook.presentation.setting.SettingViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder import dev.zacsweers.metro.Inject +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch import timber.log.Timber class MainActivity : @@ -158,8 +163,13 @@ class MainActivity : if (isDoublePress) finish() else showToast(getString(R.string.back_press_exit_message)) } } - homeViewModel.navigateToScheduleEvent.observe(this) { - binding.bnvMenu.selectedItemId = R.id.item_menu_schedule + + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + homeViewModel.navigateToScheduleEvent.collectLatest { + binding.bnvMenu.selectedItemId = R.id.item_menu_schedule + } + } } mainViewModel.isFirstVisit.observe(this) { isFirstVisit -> @@ -298,4 +308,4 @@ class MainActivity : flags = Intent.FLAG_ACTIVITY_SINGLE_TOP } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/daedan/festabook/presentation/setting/SettingFragment.kt b/app/src/main/java/com/daedan/festabook/presentation/setting/SettingFragment.kt index 0547731..5011965 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/setting/SettingFragment.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/setting/SettingFragment.kt @@ -112,25 +112,25 @@ class SettingFragment( binding.btnNoticeAllow.isEnabled = !loading } - homeViewModel.festivalUiState.observe(viewLifecycleOwner) { state -> - when (state) { - is FestivalUiState.Error -> { - showErrorSnackBar(state.throwable) - Timber.w( - state.throwable, - "${this::class.simpleName}: ${state.throwable.message}", - ) - } - - FestivalUiState.Loading -> { - binding.tvSettingCurrentUniversityNotice.text = "" - } - - is FestivalUiState.Success -> { - binding.tvSettingCurrentUniversity.text = state.organization.universityName - } - } - } +// homeViewModel.festivalUiState.observe(viewLifecycleOwner) { state -> +// when (state) { +// is FestivalUiState.Error -> { +// showErrorSnackBar(state.throwable) +// Timber.w( +// state.throwable, +// "${this::class.simpleName}: ${state.throwable.message}", +// ) +// } +// +// FestivalUiState.Loading -> { +// binding.tvSettingCurrentUniversityNotice.text = "" +// } +// +// is FestivalUiState.Success -> { +// binding.tvSettingCurrentUniversity.text = state.organization.universityName +// } +// } +// } } private fun setupServicePolicyClickListener() { From 920ea5cb630c0b1e28372f979a055b09c55bea7c Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:06:51 +0900 Subject: [PATCH 07/11] =?UTF-8?q?refactor(home):=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=BB=B4=ED=8F=AC=EC=A0=80=EB=B8=94=20=EB=B0=8F=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/daedan/festabook/presentation/home/HomeFragment.kt | 4 ++-- .../presentation/home/component/HomeFestivalInfo.kt | 2 -- .../festabook/presentation/home/component/HomeHeader.kt | 1 - .../presentation/home/component/HomeLineupHeader.kt | 4 +--- .../festabook/presentation/home/component/HomeScreen.kt | 5 ++--- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/HomeFragment.kt b/app/src/main/java/com/daedan/festabook/presentation/home/HomeFragment.kt index ed10622..fa548e6 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/HomeFragment.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/HomeFragment.kt @@ -6,9 +6,9 @@ import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.RecyclerView import com.daedan.festabook.R import com.daedan.festabook.databinding.FragmentHomeBinding import com.daedan.festabook.di.fragment.FragmentKey @@ -20,7 +20,7 @@ import dev.zacsweers.metro.ContributesIntoMap import dev.zacsweers.metro.Inject import dev.zacsweers.metro.binding -@ContributesIntoMap(scope = AppScope::class, binding = binding()) +@ContributesIntoMap(scope = AppScope::class, binding = binding()) @FragmentKey(HomeFragment::class) class HomeFragment @Inject constructor() : BaseFragment() { override val layoutId: Int = R.layout.fragment_home diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeFestivalInfo.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeFestivalInfo.kt index 7d85261..5098919 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeFestivalInfo.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeFestivalInfo.kt @@ -5,11 +5,9 @@ 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.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.daedan.festabook.presentation.theme.FestabookColor diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt index 836e48c..379b644 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt @@ -12,7 +12,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.PlatformTextStyle diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt index e0a9471..da16b70 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt @@ -1,7 +1,6 @@ package com.daedan.festabook.presentation.home.component import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -12,7 +11,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -32,7 +30,7 @@ fun HomeLineupHeader( modifier = modifier .fillMaxWidth() - .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 16.dp), + .padding(16.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt index e9e5aec..aaf0ea7 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt @@ -24,7 +24,6 @@ import com.daedan.festabook.domain.model.Organization import com.daedan.festabook.domain.model.Poster import com.daedan.festabook.presentation.home.LineupUiState import com.daedan.festabook.presentation.theme.FestabookColor -import kotlinx.coroutines.flow.collectLatest import java.time.LocalDate import java.time.LocalDateTime @@ -47,7 +46,7 @@ fun HomeScreen( } @Composable -fun FestivalOverview( +private fun FestivalOverview( festivalUiState: FestivalUiState, lineupUiState: LineupUiState, onNavigateToExplore: () -> Unit, @@ -56,7 +55,7 @@ fun FestivalOverview( ) { Scaffold( modifier = modifier.fillMaxSize(), - containerColor = Color.White, + containerColor = Color.White ) { LazyColumn( modifier = From 0ce44cf0554d2ab89bb6e63d36f93ae16528fd37 Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Mon, 29 Dec 2025 00:58:43 +0900 Subject: [PATCH 08/11] =?UTF-8?q?refactor(Home):=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20UI=20=EC=BB=B4=ED=8F=AC=EC=A6=88=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20=EB=B0=8F=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 홈 화면을 구성하는 주요 컴포저블(`HomeHeader`, `HomePosterList` 등)의 파라미터 네이밍을 정리하고, 디자인 시스템 및 커스텀 Modifier를 적용하여 코드 일관성을 높였습니다. - **`HomeHeader.kt` 수정:** - 파라미터 이름을 `schoolName`에서 `universityName`으로 변경하여 도메인 모델과의 일관성을 맞췄습니다. - 드롭다운 아이콘을 `Image`에서 `Icon`으로 변경하고, `FestabookColor`와 고정 크기(24.dp)를 적용했습니다. - **`HomePosterList.kt` 수정:** - 포스터 카드에 커스텀 Modifier인 `cardBackground`를 적용하여 배경 스타일을 개선했습니다. - **`HomeScreen.kt` 수정:** - `HomeHeader` 호출 시 변경된 파라미터 명(`universityName`)을 반영했습니다. - 리스트 하단의 여백 처리를 위해 의미상 더 적절한 `Spacer`를 사용하도록 변경했습니다. - **`HomeLineupHeader.kt` 수정:** - 일정 클릭 영역(`Row`)에서 불필요한 `padding(4.dp)`을 제거하여 레이아웃을 정돈했습니다. --- .../presentation/home/component/HomeHeader.kt | 15 +++++++++------ .../home/component/HomeLineupHeader.kt | 3 +-- .../presentation/home/component/HomePosterList.kt | 4 +++- .../presentation/home/component/HomeScreen.kt | 5 +++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt index 379b644..5677ce1 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeHeader.kt @@ -1,13 +1,14 @@ package com.daedan.festabook.presentation.home.component -import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -24,7 +25,7 @@ import com.daedan.festabook.presentation.theme.FestabookTypography @Composable fun HomeHeader( - schoolName: String, + universityName: String, onExpandClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -39,7 +40,7 @@ fun HomeHeader( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = schoolName, + text = universityName, style = FestabookTypography.displayLarge.copy( platformStyle = PlatformTextStyle(includeFontPadding = false), lineHeight = 34.sp @@ -49,9 +50,11 @@ fun HomeHeader( Spacer(modifier = Modifier.width(4.dp)) - Image( + Icon( painter = painterResource(id = R.drawable.ic_dropdown), - contentDescription = stringResource(id = R.string.home_navigate_to_explore_desc), + tint = FestabookColor.black, + contentDescription = stringResource(R.string.home_navigate_to_explore_desc), + modifier = Modifier.size(24.dp) ) } } @@ -61,7 +64,7 @@ fun HomeHeader( @Composable private fun HomeHeaderPreview() { HomeHeader( - schoolName = "가천대학교", + universityName = "가천대학교", onExpandClick = {}, ) } diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt index da16b70..3679a4a 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt @@ -45,8 +45,7 @@ fun HomeLineupHeader( Modifier .clickable( onClick = onScheduleClick, - ) - .padding(4.dp), + ), verticalAlignment = Alignment.CenterVertically, ) { Text( diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomePosterList.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomePosterList.kt index 1ce0af7..fd8b8c0 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomePosterList.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomePosterList.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.util.lerp import com.daedan.festabook.presentation.common.component.CoilImage +import com.daedan.festabook.presentation.common.component.cardBackground import kotlin.math.absoluteValue @Composable @@ -87,7 +88,8 @@ fun HomePosterList( scaleY = scale this.alpha = alpha } - .clip(RoundedCornerShape(10.dp)), + .cardBackground(roundedCornerShape = 10.dp) + .clip(RoundedCornerShape(10.dp)) ) { CoilImage( url = imageUrl, diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt index aaf0ea7..f98f657 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt @@ -1,6 +1,7 @@ package com.daedan.festabook.presentation.home.component import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -65,7 +66,7 @@ private fun FestivalOverview( item { if (festivalUiState is FestivalUiState.Success) { HomeHeader( - schoolName = festivalUiState.organization.universityName, + universityName = festivalUiState.organization.universityName, onExpandClick = onNavigateToExplore, modifier = Modifier.padding(top = 40.dp), ) @@ -144,7 +145,7 @@ private fun FestivalOverview( // 하단 여백 추가 item { - Box(modifier = Modifier.padding(bottom = 60.dp)) + Spacer(modifier = Modifier.padding(bottom = 60.dp)) } } } From e0c981e63f970a9ba50e25591240b5e3bda5b21f Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Mon, 29 Dec 2025 01:08:18 +0900 Subject: [PATCH 09/11] =?UTF-8?q?refactor(Home):=20LineupUiState=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../daedan/festabook/presentation/home/HomeViewModel.kt | 6 ++---- .../daedan/festabook/presentation/home/LineupUiState.kt | 2 +- .../festabook/presentation/home/component/HomeScreen.kt | 8 +++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/HomeViewModel.kt b/app/src/main/java/com/daedan/festabook/presentation/home/HomeViewModel.kt index 64caaff..6dbb1c1 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/HomeViewModel.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/HomeViewModel.kt @@ -61,10 +61,8 @@ class HomeViewModel @Inject constructor( val result = festivalRepository.getLineUpGroupByDate() result .onSuccess { lineups -> - _lineupUiState.value = - LineupUiState.Success( - lineups.toUiModel(), - ) + val lineupItems = lineups.toUiModel().getLineupItems() + _lineupUiState.value = LineupUiState.Success(lineupItems) }.onFailure { _lineupUiState.value = LineupUiState.Error(it) } diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/LineupUiState.kt b/app/src/main/java/com/daedan/festabook/presentation/home/LineupUiState.kt index f2b8429..dcfc03e 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/LineupUiState.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/LineupUiState.kt @@ -4,7 +4,7 @@ sealed interface LineupUiState { data object Loading : LineupUiState data class Success( - val lineups: LineUpItemGroupUiModel, + val lineups: List, ) : LineupUiState data class Error( diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt index f98f657..fe8e790 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeScreen.kt @@ -128,8 +128,10 @@ private fun FestivalOverview( // 라인업 리스트 when (lineupUiState) { is LineupUiState.Success -> { - val lineups = lineupUiState.lineups.getLineupItems() - items(lineups) { lineupItem -> + items( + items = lineupUiState.lineups, + key = { it.id }, + ) { lineupItem -> HomeLineupItem(uiModel = lineupItem) } } @@ -189,7 +191,7 @@ private fun FestivalOverviewPreview() { FestivalOverview( festivalUiState = FestivalUiState.Success(sampleFestival), - lineupUiState = LineupUiState.Success(sampleLineups), + lineupUiState = LineupUiState.Success(sampleLineups.getLineupItems()), onNavigateToExplore = {}, onNavigateToSchedule = {}, ) From a9d84b4f70d0df6c8daac327aec9d2413f5c56d0 Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Mon, 29 Dec 2025 01:17:54 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor(HomeLineupItem):=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=20=EC=98=81=EC=97=AD=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=B0=8F=20=EB=94=94=EB=B0=94=EC=9D=B4=EB=8D=94=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/component/HomeLineupItem.kt | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupItem.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupItem.kt index af9efc0..bbb106d 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupItem.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupItem.kt @@ -4,6 +4,7 @@ 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.IntrinsicSize import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -14,6 +15,8 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.DividerDefaults +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -39,44 +42,47 @@ fun HomeLineupItem( modifier = modifier.fillMaxWidth(), ) { // 날짜 + 배지 영역 - Row( - modifier = Modifier.padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically, + Column( + modifier = Modifier.padding(horizontal = 16.dp).width(IntrinsicSize.Max) ) { - Text( - text = "${uiModel.date.monthValue}.${uiModel.date.dayOfMonth}", - style = FestabookTypography.titleLarge, - color = FestabookColor.black, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth(), + ) { + Text( + text = "${uiModel.date.monthValue}.${uiModel.date.dayOfMonth}", + style = FestabookTypography.titleLarge, + color = FestabookColor.black, + ) - if (uiModel.isDDay) { - Spacer(modifier = Modifier.width(6.dp)) - Box( - modifier = - Modifier - .clip(RoundedCornerShape(20.dp)) - .background(FestabookColor.black) - .padding(horizontal = 6.dp, vertical = 2.dp), - ) { - Text( - text = stringResource(id = R.string.home_is_d_day), - style = FestabookTypography.labelSmall, - color = FestabookColor.white, - ) + if (uiModel.isDDay) { + Spacer(modifier = Modifier.width(6.dp)) + Box( + modifier = + Modifier + .clip(RoundedCornerShape(20.dp)) + .background(FestabookColor.black) + .padding(horizontal = 6.dp, vertical = 2.dp), + ) { + Text( + text = stringResource(id = R.string.home_is_d_day), + style = FestabookTypography.labelSmall, + color = FestabookColor.white, + ) + } } } + Spacer(modifier = Modifier.height(4.dp)) + + HorizontalDivider( + thickness = 1.dp, + color = FestabookColor.gray700, + modifier = Modifier.fillMaxWidth(), + ) + } - Spacer(modifier = Modifier.height(4.dp)) - Box( - modifier = - Modifier - .padding(horizontal = 16.dp) - .width(75.dp) - .height(1.dp) - .background(FestabookColor.gray700), - ) Spacer(modifier = Modifier.height(8.dp)) From f849f6f084e299637cd3c9f23d6ebb1d19cdb746 Mon Sep 17 00:00:00 2001 From: Dongjoo Seo <83579348+etama123@users.noreply.github.com> Date: Mon, 29 Dec 2025 01:26:59 +0900 Subject: [PATCH 11/11] =?UTF-8?q?refactor(Home):=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EB=A6=AC=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/home/component/HomeArtistItem.kt | 2 +- .../presentation/home/component/HomeLineupHeader.kt | 4 ++-- app/src/main/res/layout/fragment_home.xml | 2 +- app/src/main/res/values/strings.xml | 9 ++++++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeArtistItem.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeArtistItem.kt index 478baa8..7d451cc 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeArtistItem.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeArtistItem.kt @@ -51,7 +51,7 @@ fun HomeArtistItem( } } -object HomeArtistItem { +private object HomeArtistItem { val ArtistImage = RoundedCornerShape( topStartPercent = 50, topEndPercent = 50, diff --git a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt index 3679a4a..11b4d09 100644 --- a/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt +++ b/app/src/main/java/com/daedan/festabook/presentation/home/component/HomeLineupHeader.kt @@ -49,7 +49,7 @@ fun HomeLineupHeader( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = stringResource(R.string.home_check_schedule_text), + text = stringResource(R.string.home_navigate_to_schedule_text), style = FestabookTypography.bodySmall, color = FestabookColor.gray400, ) @@ -58,7 +58,7 @@ fun HomeLineupHeader( Icon( painter = painterResource(id = R.drawable.ic_arrow_forward_right), - contentDescription = null, + contentDescription = stringResource(R.string.home_navigate_to_schedule_desc), tint = FestabookColor.gray400, modifier = Modifier.size(12.dp), ) diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index e0b46fe..76e048f 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -113,7 +113,7 @@ android:background="@color/transparent" android:paddingHorizontal="16dp" android:paddingVertical="4dp" - android:text="@string/home_check_schedule_text" + android:text="@string/home_navigate_to_schedule_text" android:textColor="@color/gray400" app:icon="@drawable/ic_arrow_forward_right" app:iconGravity="end" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cca637b..5c45fca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,11 @@ poster_image 오늘 + 일정 화면으로 이동하는 버튼 + 일정 확인하기 + 축제 라인업 + 탐색 화면으로 이동하는 버튼 + 한 눈에 보기 @@ -123,8 +128,7 @@ 알림 받기 다음에 item_lineup_image - 일정 확인하기 - 축제 라인업 + 뒤로가기를 한 번 더 누르면 종료됩니다. @@ -135,7 +139,6 @@ 알림 새로운 소식이 있습니다. - 탐색 화면으로 이동하는 버튼 탐색 화면 닫기 버튼