From 514ceb3c18bafd7980c407c2f379dd714665bd51 Mon Sep 17 00:00:00 2001 From: easyhak Date: Mon, 2 Dec 2024 11:36:20 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=EB=B2=84=EC=A0=84=20=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../convention/AndroidApplicationConventionPlugin.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/convention/src/main/java/com/boostcamp/dreamteam/dreamdiary/buildlogic/convention/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/java/com/boostcamp/dreamteam/dreamdiary/buildlogic/convention/AndroidApplicationConventionPlugin.kt index 061d649c..e1934730 100644 --- a/build-logic/convention/src/main/java/com/boostcamp/dreamteam/dreamdiary/buildlogic/convention/AndroidApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/java/com/boostcamp/dreamteam/dreamdiary/buildlogic/convention/AndroidApplicationConventionPlugin.kt @@ -19,8 +19,8 @@ class AndroidApplicationConventionPlugin : Plugin { minSdk = 24 targetSdk = 35 - versionCode = 1 - versionName = "0.0.1" + versionCode = 2 + versionName = "0.0.2" } configureKotlinAndroid(this) From 966c8aa63b320b5c4c7ca9a8215ab0686d395f67 Mon Sep 17 00:00:00 2001 From: easyhak Date: Mon, 2 Dec 2024 11:36:33 +0900 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20readme=20=EA=B0=9C=ED=96=89=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 632041c1..8e1d27bb 100644 --- a/README.md +++ b/README.md @@ -94,8 +94,9 @@ **๐ŸŒ™ ๋‹คํฌ๋ชจ๋“œ** - ๋‹คํฌ ๋ชจ๋“œ ์„ ํƒ์œผ๋กœ ์‚ฌ์šฉ์ž ๋งž์ถค ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. - - + + +
From 9d874c1585860d38abe8ef5743d71a7eec45b5c2 Mon Sep 17 00:00:00 2001 From: easyhak Date: Mon, 2 Dec 2024 15:30:34 +0900 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20=EB=9D=84=EC=96=B4=EC=93=B0=EA=B8=B0?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close: #256 --- feature/setting/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/setting/src/main/res/values/strings.xml b/feature/setting/src/main/res/values/strings.xml index e5f77fa3..23f044b4 100644 --- a/feature/setting/src/main/res/values/strings.xml +++ b/feature/setting/src/main/res/values/strings.xml @@ -12,7 +12,7 @@ ๊ฟˆ ์‚ฌ์ง„์œผ๋กœ ๋ณด๊ธฐ ํ…Œ๋งˆ ์„ค์ • ์ •๋ณด - ์ž ๊ธˆ์„ค์ • + ์ž ๊ธˆ ์„ค์ • ๋กœ๊ทธ์ธ๋œ SNS ๊ณ„์ • ํ™•์ธ ๋กœ๊ทธ์•„์›ƒ ํƒˆํ‡ด From 6403af8c2bf42573c797ff818b5fdbbff9a36fe9 Mon Sep 17 00:00:00 2001 From: easyhak Date: Mon, 2 Dec 2024 15:31:49 +0900 Subject: [PATCH 4/7] =?UTF-8?q?fix:=20=EC=84=A4=EC=A0=95=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=20ripple=20=EB=8D=94=20=ED=81=AC=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close: #252 --- .../dreamteam/dreamdiary/setting/component/SettingOption.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/setting/src/main/java/com/boostcamp/dreamteam/dreamdiary/setting/component/SettingOption.kt b/feature/setting/src/main/java/com/boostcamp/dreamteam/dreamdiary/setting/component/SettingOption.kt index feb7237c..01010869 100644 --- a/feature/setting/src/main/java/com/boostcamp/dreamteam/dreamdiary/setting/component/SettingOption.kt +++ b/feature/setting/src/main/java/com/boostcamp/dreamteam/dreamdiary/setting/component/SettingOption.kt @@ -31,8 +31,8 @@ internal fun SettingOption( ) { Row( modifier = modifier - .padding(8.dp) - .clickable(onClick = onClick), + .clickable(onClick = onClick) + .padding(8.dp), verticalAlignment = Alignment.CenterVertically, ) { Icon( From a1318595372d4803ab62679589d543f970f45e1e Mon Sep 17 00:00:00 2001 From: easyhak Date: Mon, 2 Dec 2024 15:44:28 +0900 Subject: [PATCH 5/7] =?UTF-8?q?fix:=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8?= =?UTF-8?q?=20=EC=8B=9C=20=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=A7=A8=20=EC=9C=84=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close: #257 --- .../community/list/CommunityListScreen.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListScreen.kt b/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListScreen.kt index 449435d1..2ef0f3c2 100644 --- a/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListScreen.kt +++ b/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.BottomAppBarDefaults @@ -27,6 +28,10 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -218,6 +223,14 @@ private fun CommunityListScreenContent( val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior() + val listState = rememberLazyListState() + var previousRefreshState by remember { mutableStateOf(posts.loadState.refresh) } + LaunchedEffect(posts.loadState.refresh) { + if (previousRefreshState is LoadState.Loading && posts.loadState.refresh is LoadState.NotLoading) { + listState.animateScrollToItem(0) + } + previousRefreshState = posts.loadState.refresh + } Scaffold( modifier = modifier .fillMaxSize() @@ -246,12 +259,15 @@ private fun CommunityListScreenContent( ) { innerPadding -> PullToRefreshBox( isRefreshing = posts.loadState.refresh is LoadState.Loading, - onRefresh = { posts.refresh() }, + onRefresh = { + posts.refresh() + }, modifier = Modifier .fillMaxSize() .padding(innerPadding), ) { LazyColumn( + state = listState, modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), From ce874726955a411dc37c9f0776b13b4df9ebe656 Mon Sep 17 00:00:00 2001 From: easyhak Date: Mon, 2 Dec 2024 16:22:56 +0900 Subject: [PATCH 6/7] =?UTF-8?q?fix:=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8?= =?UTF-8?q?=EC=8B=9C=20=EC=A2=8B=EC=95=84=EC=9A=94=20=ED=92=80=EB=A6=AC?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close: #259 --- .../data/repository/CommunityRepository.kt | 2 +- .../usecase/community/GetPostIsLikeUseCase.kt | 18 +++++++ .../community/list/CommunityListViewModel.kt | 47 ++++++++++++------- 3 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 core/domain/src/main/java/com/boostcamp/dreamteam/dreamdiary/core/domain/usecase/community/GetPostIsLikeUseCase.kt diff --git a/core/data/src/main/java/com/boostcamp/dreamteam/dreamdiary/core/data/repository/CommunityRepository.kt b/core/data/src/main/java/com/boostcamp/dreamteam/dreamdiary/core/data/repository/CommunityRepository.kt index c6c1ca2a..2f2b887a 100644 --- a/core/data/src/main/java/com/boostcamp/dreamteam/dreamdiary/core/data/repository/CommunityRepository.kt +++ b/core/data/src/main/java/com/boostcamp/dreamteam/dreamdiary/core/data/repository/CommunityRepository.kt @@ -145,7 +145,7 @@ class CommunityRepository @Inject constructor( } } - private suspend fun checkPostLike( + suspend fun checkPostLike( postId: String, userId: String, ): Boolean { diff --git a/core/domain/src/main/java/com/boostcamp/dreamteam/dreamdiary/core/domain/usecase/community/GetPostIsLikeUseCase.kt b/core/domain/src/main/java/com/boostcamp/dreamteam/dreamdiary/core/domain/usecase/community/GetPostIsLikeUseCase.kt new file mode 100644 index 00000000..a5f49455 --- /dev/null +++ b/core/domain/src/main/java/com/boostcamp/dreamteam/dreamdiary/core/domain/usecase/community/GetPostIsLikeUseCase.kt @@ -0,0 +1,18 @@ +package com.boostcamp.dreamteam.dreamdiary.core.domain.usecase.community + +import com.boostcamp.dreamteam.dreamdiary.core.data.repository.AuthRepository +import com.boostcamp.dreamteam.dreamdiary.core.data.repository.CommunityRepository +import javax.inject.Inject + +class GetPostIsLikeUseCase @Inject constructor( + private val communityRepository: CommunityRepository, + private val authRepository: AuthRepository, +) { + suspend operator fun invoke(postId: String): Boolean { + val userId = authRepository.getUserUID() ?: throw IllegalStateException("User is not signed in") + return communityRepository.checkPostLike( + postId = postId, + userId = userId, + ) + } +} diff --git a/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListViewModel.kt b/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListViewModel.kt index 6c5f7a59..eca2ea74 100644 --- a/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListViewModel.kt +++ b/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListViewModel.kt @@ -8,6 +8,7 @@ import com.boostcamp.dreamteam.dreamdiary.community.model.PostUi import com.boostcamp.dreamteam.dreamdiary.community.model.toPostUi import com.boostcamp.dreamteam.dreamdiary.core.data.repository.AuthRepository import com.boostcamp.dreamteam.dreamdiary.core.domain.usecase.community.GetCommunityPostsUseCase +import com.boostcamp.dreamteam.dreamdiary.core.domain.usecase.community.GetPostIsLikeUseCase import com.boostcamp.dreamteam.dreamdiary.core.domain.usecase.community.TogglePostLikeUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel @@ -26,12 +27,14 @@ import javax.inject.Inject class CommunityListViewModel @Inject constructor( private val getCommunityPostsUseCase: GetCommunityPostsUseCase, private val togglePostLikeUseCase: TogglePostLikeUseCase, + private val getPostIsLikeUseCase: GetPostIsLikeUseCase, private val authRepository: AuthRepository, ) : ViewModel() { private val _event = Channel(64) val event = _event.receiveAsFlow() - private val toggledLikes = MutableStateFlow>(emptySet()) + private val toggledLikes = MutableStateFlow>(emptyMap()) + private val toggleLike = Channel() private val toggleLikeFlow = toggleLike.consumeAsFlow() .onEach { @@ -43,15 +46,20 @@ class CommunityListViewModel @Inject constructor( pagingData.map { it.toPostUi() } } .cachedIn(viewModelScope) - .combine(toggledLikes) { pagingData, toggledLikesSet -> + .combine(toggledLikes) { pagingData, toggledLikesMap -> pagingData.map { postUi -> - val newIsLiked = !postUi.isLiked - val newLikeCount = if (newIsLiked) postUi.likeCount + 1 else postUi.likeCount - 1 - if (toggledLikesSet.contains(postUi.id)) { - postUi.copy( - isLiked = newIsLiked, - likeCount = newLikeCount, - ) + val toggledIsLiked = toggledLikesMap[postUi.id] + if (toggledIsLiked != null) { + if (toggledIsLiked != postUi.isLiked) { + val likeCountDifference = if (toggledIsLiked) 1 else -1 + val newLikeCount = postUi.likeCount + likeCountDifference + postUi.copy( + isLiked = toggledIsLiked, + likeCount = newLikeCount, + ) + } else { + postUi + } } else { postUi } @@ -72,21 +80,24 @@ class CommunityListViewModel @Inject constructor( // ๋‚ด๋ถ€ ํ•จ์ˆ˜: ์‹ค์ œ๋กœ ์ข‹์•„์š” ํ† ๊ธ€ ์ฒ˜๋ฆฌ private suspend fun togglePostLikeInternal(postId: String) { - val copiedToggledLikes = toggledLikes.value.toSet() + val previousToggledLikes = toggledLikes.value.toMap() try { - toggledLikes.update { currentSet -> - if (currentSet.contains(postId)) { - currentSet - postId - } else { - currentSet + postId - } + // ํ˜„์žฌ ์ƒํƒœ ๊ฐ€์ ธ์˜ค๊ธฐ + val currentIsLiked = toggledLikes.value[postId] ?: getCurrentIsLiked(postId) + val newIsLiked = !currentIsLiked + toggledLikes.update { currentMap -> + currentMap + (postId to newIsLiked) } togglePostLikeUseCase(postId) } catch (e: Exception) { - // ์‹คํŒจํ•˜๋ฉด ๋ณต์›ํ•˜๊ธฐ - toggledLikes.value = copiedToggledLikes + // ์‹คํŒจ ์‹œ ์ด์ „ ์ƒํƒœ๋กœ ๋ณต์› + toggledLikes.value = previousToggledLikes _event.trySend(CommunityListEvent.LikePost.Failure) Timber.e(e, "Failed to toggle like for post $postId") } } + + private suspend fun getCurrentIsLiked(postId: String): Boolean { + return getPostIsLikeUseCase(postId) + } } From 2b61beb3cc102b98d41e717ce01a1f699a45181b Mon Sep 17 00:00:00 2001 From: easyhak Date: Mon, 2 Dec 2024 16:37:03 +0900 Subject: [PATCH 7/7] =?UTF-8?q?fix:=20if=20=EB=AC=B8=20=EC=A4=91=EC=B2=A9?= =?UTF-8?q?=20=ED=92=80=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/list/CommunityListViewModel.kt | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListViewModel.kt b/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListViewModel.kt index eca2ea74..f1080268 100644 --- a/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListViewModel.kt +++ b/feature/community/src/main/java/com/boostcamp/dreamteam/dreamdiary/community/list/CommunityListViewModel.kt @@ -42,30 +42,25 @@ class CommunityListViewModel @Inject constructor( } val posts = getCommunityPostsUseCase() - .map { pagingData -> - pagingData.map { it.toPostUi() } - } + .map { pagingData -> pagingData.map { it.toPostUi() } } .cachedIn(viewModelScope) .combine(toggledLikes) { pagingData, toggledLikesMap -> pagingData.map { postUi -> - val toggledIsLiked = toggledLikesMap[postUi.id] - if (toggledIsLiked != null) { - if (toggledIsLiked != postUi.isLiked) { - val likeCountDifference = if (toggledIsLiked) 1 else -1 - val newLikeCount = postUi.likeCount + likeCountDifference - postUi.copy( - isLiked = toggledIsLiked, - likeCount = newLikeCount, - ) - } else { - postUi - } - } else { - postUi - } + postUi.applyToggleLike(toggledLikesMap[postUi.id]) } } + private fun PostUi.applyToggleLike(toggleIsLike: Boolean?): PostUi { + return toggleIsLike?.let { toggled -> + if (toggled != isLiked) { + val adjustLikeCount = likeCount + if (toggled) 1 else -1 + copy(isLiked = toggled, likeCount = adjustLikeCount) + } else { + this + } + } ?: this + } + init { toggleLikeFlow.launchIn(viewModelScope) }