Skip to content

Commit 81793b7

Browse files
authored
Merge branch 'develop' into feat/#102-liked-api
2 parents bd1ccf5 + 92560b0 commit 81793b7

File tree

21 files changed

+1183
-216
lines changed

21 files changed

+1183
-216
lines changed

app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/TabListScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ fun TabListScreen(
122122
descriptionTags = post.descriptionTags,
123123
isLiked = post.isLike,
124124
onClickLike = { isLiked ->
125-
onClickLike(post.postId, isLiked)
125+
viewModel.toggleLike(post.postId, isLiked)
126+
126127
},
127128
onClickItem = { navigateToDetail() }
128129
)

app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/viewmodel/TapListViewModel.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,38 @@ class TapListViewModel @Inject constructor(
9090
_state.update { it.copy(selectedSortOption = option) }
9191
}
9292

93+
fun toggleLike(postId: Int, isLiked: Boolean) {
94+
viewModelScope.launch {
95+
_state.update { currentState ->
96+
val updatedPostsResult = currentState.postsResult?.let { postsResult ->
97+
postsResult.copy(
98+
posts = postsResult.posts.map { post ->
99+
if (post.postId == postId) {
100+
post.copy(isLike = isLiked)
101+
} else {
102+
post
103+
}
104+
}
105+
)
106+
}
107+
108+
// val updatedCourseList = currentState.courseList.map { course ->
109+
// if (course.postId == postId) {
110+
// // ArchivedListEntity의 실제 프로퍼티명에 맞게 수정
111+
// // isLiked 대신 isLike 또는 liked 등의 프로퍼티를 확인하고 사용
112+
// course.copy(isLike = isLiked) // 또는 course.copy(liked = isLiked)
113+
// } else {
114+
// course
115+
// }
116+
// }
117+
118+
currentState.copy(
119+
postsResult = updatedPostsResult,
120+
// courseList = updatedCourseList
121+
)
122+
}
123+
}
124+
}
93125
fun updateMood(option: String) {
94126
_state.update {
95127
it.copy(

app/src/main/java/com/paw/key/presentation/ui/home/HomeScreen.kt

Lines changed: 98 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
1515
import androidx.compose.foundation.layout.height
1616
import androidx.compose.foundation.layout.padding
1717
import androidx.compose.foundation.lazy.LazyColumn
18+
import androidx.compose.foundation.lazy.items
19+
import androidx.compose.material3.CircularProgressIndicator
1820
import androidx.compose.material3.Text
1921
import androidx.compose.runtime.Composable
2022
import androidx.compose.runtime.SideEffect
@@ -42,18 +44,18 @@ import com.paw.key.presentation.ui.home.component.SettingButton
4244
import com.paw.key.presentation.ui.home.component.TrackingCard
4345
import com.paw.key.presentation.ui.home.component.WeatherCard
4446
import com.paw.key.presentation.ui.home.viewmodel.HomeViewModel
45-
import kotlin.String
46-
4747

4848
@Preview
4949
@Composable
5050
private fun HomeScreenPreview() {
5151
PawKeyTheme {
5252
HomeScreen(
5353
paddingValues = PaddingValues(),
54+
onClickLike = { _, _ -> },
5455
navigateUp = {},
5556
navigateNext = {},
56-
navigateHomeLocationSetting = {}
57+
navigateHomeLocationSetting = {},
58+
viewModel = hiltViewModel()
5759
)
5860
}
5961
}
@@ -67,14 +69,16 @@ fun HomeRoute(
6769
modifier: Modifier = Modifier,
6870
viewModel: HomeViewModel = hiltViewModel(),
6971
) {
70-
7172
HomeScreen(
7273
paddingValues = paddingValues,
7374
navigateUp = navigateUp,
7475
navigateNext = navigateNext,
7576
navigateHomeLocationSetting = navigateHomeLocationSetting,
7677
modifier = modifier,
77-
viewModel = viewModel
78+
viewModel = viewModel,
79+
onClickLike = { postId, isLiked ->
80+
viewModel.toggleLike(postId = postId, isLiked = isLiked)
81+
}
7882
)
7983
}
8084

@@ -86,92 +90,120 @@ fun HomeScreen(
8690
navigateHomeLocationSetting: () -> Unit,
8791
modifier: Modifier = Modifier,
8892
viewModel: HomeViewModel = hiltViewModel(),
93+
onClickLike: (postId: Int, isLiked: Boolean) -> Unit
8994
) {
9095
val state by viewModel.state.collectAsStateWithLifecycle()
9196
val view = LocalView.current
9297
val window = (view.context as? Activity)?.window
98+
val postsResult = state.postsResult
99+
val posts = postsResult?.posts
93100

94101
SideEffect {
95102
window?.let {
96103
it.statusBarColor = Color.Black.toArgb()
97-
ViewCompat.getWindowInsetsController(view)?.let { controller ->
98-
controller.isAppearanceLightStatusBars = false
99-
}
104+
ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = false
100105
}
101106
}
102107

103108
Column(
104109
modifier = modifier
105110
.padding(paddingValues)
106-
.background(color = PawKeyTheme.colors.white2)
111+
.background(PawKeyTheme.colors.white2)
107112
.fillMaxSize()
108113
) {
109-
HomeTopBar(location = "강남구 역삼동", onLocationClick = { viewModel.toggleLocationMenu() })
110-
111-
LazyColumn (
112-
verticalArrangement = Arrangement.spacedBy(12.dp),
113-
modifier = Modifier
114-
.padding(horizontal = 16.dp)
115-
.background(color = PawKeyTheme.colors.white2),
116-
) {
117-
item{
118-
Spacer(modifier = Modifier.height(12.dp))
114+
HomeTopBar(
115+
location = state.selectedLocation.displayLocation,
116+
onLocationClick = { viewModel.toggleLocationMenu() }
117+
)
119118

120-
WeatherCard(
121-
weathertitle = "35°",
122-
weathersub1 = "35°",
123-
weathersub2 = "21°",
124-
rating = "0",
125-
weatherIcon = R.drawable.ic_home_weather,
126-
)
119+
// 로딩 상태 표시
120+
if (state.uiState.isLoading) {
121+
Box(
122+
modifier = Modifier.fillMaxSize(),
123+
contentAlignment = Alignment.Center
124+
) {
125+
CircularProgressIndicator()
127126
}
128-
item{
129-
Spacer(modifier = Modifier.height(12.dp))
127+
} else {
128+
LazyColumn(
129+
verticalArrangement = Arrangement.spacedBy(12.dp),
130+
modifier = Modifier
131+
.padding(horizontal = 16.dp)
132+
.background(PawKeyTheme.colors.white2),
133+
) {
134+
item {
135+
Spacer(modifier = Modifier.height(12.dp))
136+
WeatherCard(
137+
weathertitle = "35°",
138+
weathersub1 = "35°",
139+
weathersub2 = "21°",
140+
rating = "0",
141+
weatherIcon = R.drawable.ic_home_weather,
142+
)
143+
}
144+
145+
item {
146+
Spacer(modifier = Modifier.height(12.dp))
147+
Row(
148+
modifier = Modifier.fillMaxWidth()
149+
) {
150+
DaytimeCard(daytime = "05:06", daystate = "일출")
151+
Spacer(modifier = Modifier.weight(1F))
152+
TrackingCard(onClick = { navigateNext() })
153+
}
154+
}
130155

131-
Row(
132-
modifier = Modifier
133-
.fillMaxWidth()
134-
) {
135-
DaytimeCard(
136-
daytime = "05:06",
137-
daystate = "일출",
156+
item { RowCalendar(date = "7월") }
157+
158+
item {
159+
Spacer(modifier = Modifier.height(12.dp))
160+
Text(
161+
text = stringResource(R.string.ic_home_current_word),
162+
color = PawKeyTheme.colors.black,
163+
style = PawKeyTheme.typography.head18Sb,
138164
)
165+
}
139166

140-
Spacer(modifier = Modifier.weight(1F))
167+
// 에러 상태 표시
168+
state.uiState.error?.let { error ->
169+
item {
170+
Text(
171+
text = "오류: $error",
172+
color = Color.Red,
173+
modifier = Modifier.padding(16.dp)
174+
)
175+
}
176+
}
141177

142-
TrackingCard(onClick = { navigateNext() })
178+
// posts가 null이 아닐 때만 items를 표시
179+
posts?.let { postList ->
180+
items(
181+
items = postList,
182+
key = { post -> post.postId }
183+
) { post ->
184+
CourseCard(
185+
postId = post.postId,
186+
title = post.title,
187+
petName = post.writer.petName,
188+
createdAt = post.createdAt,
189+
representativeImageUrl = post.representativeImageUrl,
190+
petProfileImageUrl = post.writer.petProfileImageUrl,
191+
descriptionTags = post.descriptionTags,
192+
isLiked = post.isLike,
193+
onClickLike = { isLiked ->
194+
onClickLike(post.postId, isLiked)
195+
},
196+
onClickItem = { navigateNext() }
197+
)
198+
}
143199
}
144-
}
145-
item{
146-
RowCalendar(date = "7월")
147-
}
148-
item{
149-
Spacer(modifier = Modifier.height(12.dp))
150200

151-
Text(
152-
text = stringResource(R.string.ic_home_current_word),
153-
color = PawKeyTheme.colors.black,
154-
style = PawKeyTheme.typography.head18Sb,
155-
)
201+
item { Spacer(modifier = Modifier.height(48.dp)) }
156202
}
157-
item{
158-
CourseCard(
159-
title = "제목을 입력해주세요",
160-
petName = "반려견 이름",
161-
createdAt = "년도/월/일",
162-
representativeImageUrl = "시:분",
163-
petProfileImageUrl = "",
164-
descriptionTags = listOf("이륜차 거의 없음", "물그릇 비치", "쉴 곳 있음"),
165-
onClickItem = {},
166-
isLiked = true,
167-
postId = 1
168-
)
169-
Spacer(modifier = Modifier.height(48.dp))
170-
}
171-
172203
}
173-
174204
}
205+
206+
// 위치 메뉴 오버레이
175207
if (state.isLocationMenuVisible) {
176208
Box(
177209
modifier = Modifier
@@ -180,9 +212,7 @@ fun HomeScreen(
180212
.clickable(
181213
indication = null,
182214
interactionSource = remember { MutableInteractionSource() }
183-
) {
184-
viewModel.toggleLocationMenu()
185-
}
215+
) { viewModel.toggleLocationMenu() }
186216
) {
187217
SettingButton(
188218
modifier = Modifier
@@ -195,4 +225,4 @@ fun HomeScreen(
195225
)
196226
}
197227
}
198-
}
228+
}
Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,66 @@
1+
12
package com.paw.key.presentation.ui.home.state
23

34
import androidx.compose.runtime.Immutable
4-
5+
import com.paw.key.domain.model.entity.archivedlist.ArchivedListEntity
6+
import com.paw.key.domain.model.entity.list.ListEntity
57

68
class HomeContract {
9+
710
@Immutable
811
data class HomeState(
912
val isLocationMenuVisible: Boolean = false,
10-
val isVisible: Boolean = false,
11-
val selectedLocation: String = "",
13+
val postsResult: ListEntity? = null,
14+
val selectedLocation: LocationInfo = LocationInfo(),
15+
val courseList: List<ArchivedListEntity> = emptyList(),
16+
val uiState: HomeUiState = HomeUiState(),
17+
val selectedGuId: Int = 0,
18+
val selectedDongId: Int = 0,
19+
val selectedGu: String = "",
20+
val selectedDong: String = ""
21+
)
1222

23+
@Immutable
24+
data class LocationInfo(
1325
val selectedGuId: Int = 0,
1426
val selectedDongId: Int = 0,
1527
val selectedGu: String = "",
16-
val selectedDong: String = "",
28+
val selectedDong: String = ""
29+
) {
30+
val displayLocation: String
31+
get() = if (selectedGu.isNotEmpty() && selectedDong.isNotEmpty()) {
32+
"$selectedGu $selectedDong"
33+
} else if (selectedGu.isNotEmpty()) {
34+
selectedGu
35+
} else {
36+
"위치를 선택해주세요"
37+
}
1738

39+
val isLocationSelected: Boolean
40+
get() = selectedGuId != 0 && selectedDongId != 0
41+
}
42+
43+
@Immutable
44+
data class HomeUiState(
45+
val isVisible: Boolean = false,
46+
val isLoading: Boolean = false,
47+
val error: String? = null
1848
)
19-
}
49+
50+
// 액션들을 정의하는 sealed class
51+
sealed class HomeAction {
52+
object ToggleLocationMenu : HomeAction()
53+
data class SelectGu(val guName: String, val guId: Int) : HomeAction()
54+
data class SelectDong(val dongName: String, val dongId: Int) : HomeAction()
55+
data class ToggleLike(val postId: Int, val isLiked: Boolean) : HomeAction()
56+
object RefreshPosts : HomeAction()
57+
object ClearError : HomeAction()
58+
}
59+
60+
// 사이드 이펙트를 정의하는 sealed class
61+
sealed class HomeSideEffect {
62+
object NavigateToLocationSetting : HomeSideEffect()
63+
data class ShowError(val message: String) : HomeSideEffect()
64+
data class ShowToast(val message: String) : HomeSideEffect()
65+
}
66+
}

0 commit comments

Comments
 (0)