Skip to content

Commit 0cca857

Browse files
committed
[NDGL-61] refactor: Home 화면 기본 구조 구현
1 parent 0d5d6eb commit 0cca857

File tree

7 files changed

+358
-95
lines changed

7 files changed

+358
-95
lines changed

feature/home/src/main/java/com/yapp/ndgl/feature/home/HomeContract.kt

Lines changed: 0 additions & 13 deletions
This file was deleted.

feature/home/src/main/java/com/yapp/ndgl/feature/home/HomeScreen.kt

Lines changed: 0 additions & 46 deletions
This file was deleted.

feature/home/src/main/java/com/yapp/ndgl/feature/home/HomeViewModel.kt

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.yapp.ndgl.feature.home.main
2+
3+
import androidx.annotation.DrawableRes
4+
import androidx.compose.runtime.Immutable
5+
import androidx.compose.runtime.Stable
6+
import com.yapp.ndgl.core.base.UiIntent
7+
import com.yapp.ndgl.core.base.UiSideEffect
8+
import com.yapp.ndgl.core.base.UiState
9+
import com.yapp.ndgl.data.travel.model.TravelSummary
10+
import java.time.LocalDate
11+
12+
@Stable
13+
data class HomeState(
14+
val userName: String = "",
15+
val myTravel: MyTravel = MyTravel.None,
16+
val popularTravelSelectedTabIndex: Int = 0,
17+
val popularTravelTabs: List<PopularTravelTab> = emptyList(),
18+
val popularTravelsByTab: Map<String, List<TravelSummary>> = emptyMap(),
19+
val recommendedContents: List<TravelSummary> = emptyList(),
20+
) : UiState {
21+
@Stable
22+
sealed interface MyTravel {
23+
@Immutable
24+
data object None : MyTravel
25+
26+
@Immutable
27+
data class Upcoming(
28+
val title: String,
29+
val imageUrl: String,
30+
val dDay: Int,
31+
val startDate: LocalDate,
32+
val endDate: LocalDate,
33+
) : MyTravel
34+
35+
@Immutable
36+
data class InProgress(
37+
val title: String,
38+
val dayCount: Int,
39+
val startDate: LocalDate,
40+
val endDate: LocalDate,
41+
val currentPlace: TravelPlace,
42+
) : MyTravel
43+
}
44+
45+
data class TravelPlace(
46+
val category: String,
47+
val estimatedTime: String,
48+
val name: String,
49+
val thumbnailUrl: String,
50+
)
51+
52+
data class PopularTravelTab(
53+
val tag: String,
54+
val name: String,
55+
@DrawableRes val icon: Int? = null,
56+
)
57+
}
58+
59+
sealed interface HomeIntent : UiIntent {
60+
data class SelectPopularTravelTab(val index: Int) : HomeIntent
61+
}
62+
63+
sealed interface HomeSideEffect : UiSideEffect
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package com.yapp.ndgl.feature.home.main
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.PaddingValues
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.lazy.LazyColumn
9+
import androidx.compose.material3.Scaffold
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.runtime.getValue
12+
import androidx.compose.ui.Modifier
13+
import androidx.compose.ui.tooling.preview.Preview
14+
import androidx.compose.ui.unit.dp
15+
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
16+
import com.yapp.ndgl.core.ui.R
17+
import com.yapp.ndgl.core.ui.designsystem.NDGLNavigationBar
18+
import com.yapp.ndgl.core.ui.designsystem.NDGLNavigationBarAttr
19+
import com.yapp.ndgl.core.ui.designsystem.NDGLNavigationIcon
20+
import com.yapp.ndgl.core.ui.theme.NDGLTheme
21+
import com.yapp.ndgl.data.travel.model.TravelSummary
22+
import java.time.LocalDate
23+
24+
@Composable
25+
internal fun HomeRoute(
26+
viewModel: HomeViewModel = hiltViewModel(),
27+
innerPadding: PaddingValues,
28+
) {
29+
val state by viewModel.collectAsState()
30+
HomeScreen(
31+
state = state,
32+
onTabSelected = { index ->
33+
viewModel.onIntent(HomeIntent.SelectPopularTravelTab(index))
34+
},
35+
)
36+
}
37+
38+
@Composable
39+
private fun HomeScreen(
40+
state: HomeState = HomeState(),
41+
onTabSelected: (Int) -> Unit = {},
42+
) {
43+
Scaffold(
44+
topBar = {
45+
NDGLNavigationBar(
46+
textAlignType = NDGLNavigationBarAttr.TextAlignType.START,
47+
modifier = Modifier
48+
.fillMaxWidth()
49+
.background(color = NDGLTheme.colors.white),
50+
trailingContents = {
51+
NDGLNavigationIcon(
52+
icon = R.drawable.ic_28_search,
53+
onClick = { /* FIXME: 홈 검색 */ },
54+
)
55+
NDGLNavigationIcon(
56+
icon = R.drawable.ic_28_settings,
57+
onClick = { /* FIXME: 설정 */ },
58+
)
59+
},
60+
)
61+
},
62+
) { innerPadding ->
63+
LazyColumn(
64+
modifier = Modifier.fillMaxSize(),
65+
contentPadding = PaddingValues(
66+
top = innerPadding.calculateTopPadding() + 20.dp,
67+
bottom = 80.dp,
68+
),
69+
verticalArrangement = Arrangement.spacedBy(40.dp),
70+
) {
71+
item {
72+
MyTravelCardSection(
73+
modifier = Modifier.fillMaxWidth(),
74+
myTravel = state.myTravel,
75+
)
76+
}
77+
78+
item {
79+
if (state.popularTravelsByTab.isNotEmpty()) {
80+
PopularTravelSection(
81+
tabs = state.popularTravelTabs,
82+
selectedTabIndex = state.popularTravelSelectedTabIndex,
83+
travelsByTab = state.popularTravelsByTab,
84+
onTabSelected = onTabSelected,
85+
)
86+
}
87+
}
88+
89+
if (state.recommendedContents.isNotEmpty()) {
90+
item {
91+
RecommendedContentSection(
92+
userName = state.userName,
93+
contents = state.recommendedContents,
94+
)
95+
}
96+
}
97+
}
98+
}
99+
}
100+
101+
@Preview(showBackground = true)
102+
@Composable
103+
private fun HomeScreenPreview() {
104+
val sampleTravels = listOf(
105+
TravelSummary(
106+
travelId = "1",
107+
country = "FR",
108+
city = "파리",
109+
nights = 7,
110+
days = 9,
111+
youtube = TravelSummary.YoutubeInfo(
112+
title = "곽준빈의 신혼여행",
113+
youtuber = "곽튜브",
114+
thumbnail = "https://picsum.photos/200/300",
115+
),
116+
),
117+
TravelSummary(
118+
travelId = "2",
119+
country = "CH",
120+
city = "스위스",
121+
nights = 5,
122+
days = 6,
123+
youtube = TravelSummary.YoutubeInfo(
124+
title = "스위스 여행",
125+
youtuber = "빠니보틀",
126+
thumbnail = "https://picsum.photos/200/300",
127+
),
128+
),
129+
TravelSummary(
130+
travelId = "3",
131+
country = "DK",
132+
city = "덴마크",
133+
nights = 4,
134+
days = 6,
135+
youtube = TravelSummary.YoutubeInfo(
136+
title = "충격적인 북유럽 물가",
137+
youtuber = "곽튜브",
138+
thumbnail = "https://picsum.photos/200/300",
139+
),
140+
),
141+
)
142+
143+
NDGLTheme {
144+
HomeScreen(
145+
state = HomeState(
146+
userName = "유저123",
147+
myTravel = HomeState.MyTravel.InProgress(
148+
title = "인도 여행",
149+
dayCount = 1,
150+
startDate = LocalDate.of(2024, 12, 23),
151+
endDate = LocalDate.of(2024, 12, 26),
152+
currentPlace = HomeState.TravelPlace(
153+
category = "교통수단",
154+
estimatedTime = "1시간 체류 예상",
155+
name = "인도 국제 공항",
156+
thumbnailUrl = "",
157+
),
158+
),
159+
popularTravelTabs = listOf(
160+
HomeState.PopularTravelTab(tag = "all", name = "전체"),
161+
HomeState.PopularTravelTab(tag = "ppanibottle", name = "빠니보틀", icon = R.drawable.ic_20_video),
162+
HomeState.PopularTravelTab(tag = "gwaktube", name = "곽튜브", icon = R.drawable.ic_20_video),
163+
HomeState.PopularTravelTab(tag = "kongkong", name = "콩콩팡팡", icon = R.drawable.ic_20_tv),
164+
),
165+
popularTravelsByTab = mapOf(
166+
"all" to sampleTravels,
167+
"ppanibottle" to sampleTravels.filter { it.youtube.youtuber == "빠니보틀" },
168+
"gwaktube" to sampleTravels.filter { it.youtube.youtuber == "곽튜브" },
169+
),
170+
recommendedContents = sampleTravels.take(2),
171+
),
172+
)
173+
}
174+
}

0 commit comments

Comments
 (0)