From 941394eccbe3c88ea665d6889fb92fd74b43f17e Mon Sep 17 00:00:00 2001 From: leeeha Date: Mon, 21 Aug 2023 03:32:01 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[feat]=20#132=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20post,=20=EC=96=B4=EB=8C=91=ED=84=B0?= =?UTF-8?q?=EB=A1=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/response/ResponsePostCommentDto.kt | 11 +++++++- .../data/repository/FeedRepositoryImpl.kt | 8 +++--- .../go/sopt/winey/data/service/FeedService.kt | 2 +- .../sopt/winey/data/source/FeedDataSource.kt | 2 +- .../winey/domain/repository/FeedRepository.kt | 6 ++--- .../main/feed/detail/CommentAdapter.kt | 13 +++++++++ .../main/feed/detail/DetailActivity.kt | 27 +++++++++++++++++++ .../main/feed/detail/DetailViewModel.kt | 24 +++++++++++++++++ app/src/main/res/values/strings.xml | 8 +++--- 9 files changed, 87 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponsePostCommentDto.kt b/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponsePostCommentDto.kt index 58bfcc06..134ee0fc 100644 --- a/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponsePostCommentDto.kt +++ b/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponsePostCommentDto.kt @@ -1,5 +1,6 @@ package com.android.go.sopt.winey.data.model.remote.response +import com.android.go.sopt.winey.domain.entity.Comment import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -17,4 +18,12 @@ data class ResponsePostCommentDto( val authorId: Int, @SerialName("authorLevel") val authorLevel: Int -) +) { + fun toComment() = Comment( + commentId = this.commentId, + author = this.author, + content = this.content, + authorLevel = this.authorLevel, + authorId = this.authorId + ) +} diff --git a/app/src/main/java/com/android/go/sopt/winey/data/repository/FeedRepositoryImpl.kt b/app/src/main/java/com/android/go/sopt/winey/data/repository/FeedRepositoryImpl.kt index 1c81d7c0..4cfb034d 100644 --- a/app/src/main/java/com/android/go/sopt/winey/data/repository/FeedRepositoryImpl.kt +++ b/app/src/main/java/com/android/go/sopt/winey/data/repository/FeedRepositoryImpl.kt @@ -5,12 +5,12 @@ import androidx.paging.PagingConfig import androidx.paging.PagingData import com.android.go.sopt.winey.data.model.remote.request.RequestPostCommentDto import com.android.go.sopt.winey.data.model.remote.request.RequestPostLikeDto -import com.android.go.sopt.winey.data.model.remote.response.ResponsePostCommentDto import com.android.go.sopt.winey.data.model.remote.response.ResponsePostWineyFeedDto import com.android.go.sopt.winey.data.service.FeedService import com.android.go.sopt.winey.data.source.FeedDataSource import com.android.go.sopt.winey.data.source.paging.MyFeedPagingSource import com.android.go.sopt.winey.data.source.paging.WineyFeedPagingSource +import com.android.go.sopt.winey.domain.entity.Comment import com.android.go.sopt.winey.domain.entity.DetailFeed import com.android.go.sopt.winey.domain.entity.Like import com.android.go.sopt.winey.domain.entity.WineyFeed @@ -61,11 +61,11 @@ class FeedRepositoryImpl @Inject constructor( } override suspend fun postComment( - feedId: Long, + feedId: Int, requestPostCommentDto: RequestPostCommentDto - ): Result = + ): Result = runCatching { - feedDataSource.postComment(feedId, requestPostCommentDto).data + feedDataSource.postComment(feedId, requestPostCommentDto).data?.toComment() } companion object { diff --git a/app/src/main/java/com/android/go/sopt/winey/data/service/FeedService.kt b/app/src/main/java/com/android/go/sopt/winey/data/service/FeedService.kt index 0862f5de..77f19fe4 100644 --- a/app/src/main/java/com/android/go/sopt/winey/data/service/FeedService.kt +++ b/app/src/main/java/com/android/go/sopt/winey/data/service/FeedService.kt @@ -56,7 +56,7 @@ interface FeedService { @POST("comment/{feedId}") suspend fun postComment( - @Path("feedId") feedId: Long, + @Path("feedId") feedId: Int, @Body requestPostCommentDto: RequestPostCommentDto ): BaseResponse } diff --git a/app/src/main/java/com/android/go/sopt/winey/data/source/FeedDataSource.kt b/app/src/main/java/com/android/go/sopt/winey/data/source/FeedDataSource.kt index c1d9699a..faa04a71 100644 --- a/app/src/main/java/com/android/go/sopt/winey/data/source/FeedDataSource.kt +++ b/app/src/main/java/com/android/go/sopt/winey/data/source/FeedDataSource.kt @@ -36,7 +36,7 @@ class FeedDataSource @Inject constructor( feedService.getFeedDetail(feedId) suspend fun postComment( - feedId: Long, + feedId: Int, requestPostCommentDto: RequestPostCommentDto ): BaseResponse = feedService.postComment(feedId, requestPostCommentDto) diff --git a/app/src/main/java/com/android/go/sopt/winey/domain/repository/FeedRepository.kt b/app/src/main/java/com/android/go/sopt/winey/domain/repository/FeedRepository.kt index e513e1ea..438d82e9 100644 --- a/app/src/main/java/com/android/go/sopt/winey/domain/repository/FeedRepository.kt +++ b/app/src/main/java/com/android/go/sopt/winey/domain/repository/FeedRepository.kt @@ -3,8 +3,8 @@ package com.android.go.sopt.winey.domain.repository import androidx.paging.PagingData import com.android.go.sopt.winey.data.model.remote.request.RequestPostCommentDto import com.android.go.sopt.winey.data.model.remote.request.RequestPostLikeDto -import com.android.go.sopt.winey.data.model.remote.response.ResponsePostCommentDto import com.android.go.sopt.winey.data.model.remote.response.ResponsePostWineyFeedDto +import com.android.go.sopt.winey.domain.entity.Comment import com.android.go.sopt.winey.domain.entity.DetailFeed import com.android.go.sopt.winey.domain.entity.Like import com.android.go.sopt.winey.domain.entity.WineyFeed @@ -29,7 +29,7 @@ interface FeedRepository { suspend fun getFeedDetail(feedId: Int): Result suspend fun postComment( - feedId: Long, + feedId: Int, requestPostCommentDto: RequestPostCommentDto - ): Result + ): Result } diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt index 7d9284aa..39ca2c96 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt @@ -31,6 +31,19 @@ class CommentAdapter : ListAdapter(di holder.onBind(getItem(position)) } + fun addItem(item: Comment) { + val newList = currentList.toMutableList() + newList.add(item) + submitList(newList) + } + + // todo: 어댑터에서 아이템 삭제한 뒤에, 실제로 서버통신도 해줘야 다음에 GET 할 때 반영된다! + fun deleteItem(position: Int) { + val newList = currentList.toMutableList() + newList.removeAt(position) + submitList(newList) + } + companion object { private val diffUtil = ItemDiffCallback( onItemsTheSame = { old, new -> old.commentId == new.commentId }, diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt index aa558f7d..10a6b15f 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt @@ -36,6 +36,33 @@ class DetailActivity : BindingActivity(R.layout.activity_ viewModel.getFeedDetail(feedId) initGetFeedDetailObserver() initBackButtonClickListener() + + initCommentUploadButtonClickListener() + initPostCommentStateObserver() + } + + private fun initCommentUploadButtonClickListener() { + binding.tvCommentUpload.setOnClickListener { + val content = binding.etComment.text.toString() + viewModel.postComment(feedId, content) + } + } + + private fun initPostCommentStateObserver() { + viewModel.postCommentState.flowWithLifecycle(lifecycle) + .onEach { state -> + when (state) { + is UiState.Success -> { + val comment = state.data ?: return@onEach + commentAdapter.addItem(comment) + } + is UiState.Failure -> { + snackBar(binding.root) { state.msg } + } + else -> { + } + } + }.launchIn(lifecycleScope) } private fun initGetFeedDetailObserver() { diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailViewModel.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailViewModel.kt index 5e25da8a..dcb07797 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailViewModel.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailViewModel.kt @@ -2,6 +2,8 @@ package com.android.go.sopt.winey.presentation.main.feed.detail import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.android.go.sopt.winey.data.model.remote.request.RequestPostCommentDto +import com.android.go.sopt.winey.domain.entity.Comment import com.android.go.sopt.winey.domain.entity.DetailFeed import com.android.go.sopt.winey.domain.repository.FeedRepository import com.android.go.sopt.winey.util.view.UiState @@ -49,6 +51,9 @@ class DetailViewModel @Inject constructor( val getFeedDetailState: StateFlow> = _getFeedDetailState.asStateFlow() + private val _postCommentState = MutableStateFlow>(UiState.Loading) + val postCommentState: StateFlow> = _postCommentState.asStateFlow() + fun getFeedDetail(feedId: Int) { viewModelScope.launch { feedRepository.getFeedDetail(feedId) @@ -59,6 +64,25 @@ class DetailViewModel @Inject constructor( } } + fun postComment(feedId: Int, content: String) { + viewModelScope.launch { + feedRepository.postComment(feedId, RequestPostCommentDto(content)) + .onSuccess { response -> + _postCommentState.value = UiState.Success(response) + Timber.d("SUCCESS POST COMMENT") + } + .onFailure { t -> + _postCommentState.value = UiState.Failure(t.message.toString()) + + if (t is HttpException) { + Timber.e("HTTP FAIL POST COMMENT: ${t.code()} ${t.message}") + return@onFailure + } + Timber.e("FAIL POST COMMENT: ${t.message}") + } + } + } + private fun handleFailureState(loadingState: MutableStateFlow>, t: Throwable) { if (t is HttpException) { val errorMessage = when (t.code()) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 42e05b6a..a87fcf5e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -189,10 +189,10 @@ 죄송합니다. 업로드에 실패했습니다 :( - LV.평민 ㆍ - LV.기사 ㆍ - LV.귀족 ㆍ - LV.황제 ㆍ + LV. 평민 ㆍ + LV. 기사 ㆍ + LV. 귀족 ㆍ + LV. 황제 ㆍ 아직 댓글이 없어요 댓글을 입력하세요 등록 From ef8bf13d0571721a133b66e1d3142336466f3b3a Mon Sep 17 00:00:00 2001 From: leeeha Date: Mon, 21 Aug 2023 16:43:51 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[ui]=20#133=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=ED=8C=9D=EC=97=85=EB=A9=94=EB=89=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../go/sopt/winey/util/view/WineyPopupMenu.kt | 37 +++++++++++++++++++ app/src/main/res/layout/item_winey_popup.xml | 30 +++++++++++++++ .../main/res/layout/layout_winey_popup.xml | 15 ++++++++ app/src/main/res/layout/menu_wineyfeed.xml | 19 +++++----- 4 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt create mode 100644 app/src/main/res/layout/item_winey_popup.xml create mode 100644 app/src/main/res/layout/layout_winey_popup.xml diff --git a/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt b/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt new file mode 100644 index 00000000..37d6e1bb --- /dev/null +++ b/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt @@ -0,0 +1,37 @@ +package com.android.go.sopt.winey.util.view + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.PopupWindow +import com.android.go.sopt.winey.databinding.ItemWineyPopupBinding +import com.android.go.sopt.winey.databinding.LayoutWineyPopupBinding + +class WineyPopupMenu( + private val context: Context, + private val titles: MutableList, + private val menuItemClickListener: (View, String, Int) -> Unit +) : PopupWindow(context) { + init { + addMenuItemViewToLayout() + } + + private fun addMenuItemViewToLayout() { + val inflater = LayoutInflater.from(context) + val layoutBinding = LayoutWineyPopupBinding.inflate(inflater, null, false) + val itemBinding = ItemWineyPopupBinding.inflate(inflater, null, false) + + for (i in 0 until titles.size) { + itemBinding.tvPopupTitle.text = titles[i] + itemBinding.vPopupSeperator.visibility = + if (i < titles.size - 1) View.VISIBLE else View.INVISIBLE + + itemBinding.root.setOnClickListener { rootView -> + menuItemClickListener.invoke(rootView, titles[i], i) + dismiss() + } + + layoutBinding.llPopupContainer.addView(itemBinding.root) + } + } +} diff --git a/app/src/main/res/layout/item_winey_popup.xml b/app/src/main/res/layout/item_winey_popup.xml new file mode 100644 index 00000000..e7303abb --- /dev/null +++ b/app/src/main/res/layout/item_winey_popup.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/app/src/main/res/layout/layout_winey_popup.xml b/app/src/main/res/layout/layout_winey_popup.xml new file mode 100644 index 00000000..93ba08e6 --- /dev/null +++ b/app/src/main/res/layout/layout_winey_popup.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/layout/menu_wineyfeed.xml b/app/src/main/res/layout/menu_wineyfeed.xml index 0b051d63..1ec8990d 100644 --- a/app/src/main/res/layout/menu_wineyfeed.xml +++ b/app/src/main/res/layout/menu_wineyfeed.xml @@ -3,7 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" - app:cardCornerRadius="6dp"> + app:cardCornerRadius="6dp" + app:cardElevation="4dp"> + android:text="@string/wineyfeed_menu_delete" + android:textAppearance="@style/TextAppearance.WINEY.detail_m_12" /> + android:background="@color/gray_200" + android:visibility="invisible" /> + android:text="@string/wineyfeed_menu_report" + android:textAppearance="@style/TextAppearance.WINEY.detail_m_12" /> From 792a5ed5c1616b5ebedeec4aa37ea42c83a5e8f2 Mon Sep 17 00:00:00 2001 From: leeeha Date: Mon, 21 Aug 2023 20:10:31 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[feat]=20#132=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=ED=8C=9D=EC=97=85=EB=A9=94=EB=89=B4=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(=EA=B7=B8=EB=A6=BC=EC=9E=90=20=ED=9A=A8=EA=B3=BC=EA=B9=8C?= =?UTF-8?q?=EC=A7=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/feed/WineyFeedAdapter.kt | 4 +- .../main/feed/WineyFeedFragment.kt | 8 +- .../main/feed/detail/CommentAdapter.kt | 17 ++- .../main/feed/detail/DetailActivity.kt | 142 +++++++++++++++++- .../go/sopt/winey/util/view/WineyPopupMenu.kt | 44 ++++-- .../res/drawable/shape_white_fill_5_rect.xml | 6 + app/src/main/res/layout/activity_detail.xml | 4 +- app/src/main/res/layout/fragment_detail.xml | 2 +- app/src/main/res/layout/item_winey_popup.xml | 2 +- .../main/res/layout/layout_winey_popup.xml | 15 +- app/src/main/res/values/strings.xml | 18 ++- 11 files changed, 219 insertions(+), 43 deletions(-) create mode 100644 app/src/main/res/drawable/shape_white_fill_5_rect.xml diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedAdapter.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedAdapter.kt index b5b0152f..b1d3d017 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedAdapter.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedAdapter.kt @@ -24,7 +24,7 @@ class WineyFeedAdapter( private val binding: ItemWineyfeedPostBinding, private val onLikeButtonClick: (feedId: Int, isLiked: Boolean) -> Unit, private val showPopupMenu: (View, WineyFeed) -> Unit, - private val toFeedDetail: (feedId: Int, writerLevel: Int) -> Unit + private val toFeedDetail: (feedId: Int, writerId: Int) -> Unit ) : RecyclerView.ViewHolder(binding.root) { fun onBind(data: WineyFeed?) { @@ -43,7 +43,7 @@ class WineyFeedAdapter( } lWineyfeedPost.setOnSingleClickListener { - toFeedDetail(data.feedId, data.writerLevel) + toFeedDetail(data.feedId, data.userId) } } } diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt index 1fb4b0ba..64adf881 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt @@ -81,7 +81,7 @@ class WineyFeedFragment : BindingFragment(R.layout.fra showPopupMenu = { view, wineyFeed -> showPopupMenu(view, wineyFeed) }, - toFeedDetail = { feedId, writerLevel -> navigateToDetail(feedId, writerLevel) } + toFeedDetail = { feedId, writerId -> navigateToDetail(feedId, writerId) } ) binding.rvWineyfeedPost.adapter = ConcatAdapter( wineyFeedHeaderAdapter, @@ -266,10 +266,10 @@ class WineyFeedFragment : BindingFragment(R.layout.fra startActivity(intent) } - private fun navigateToDetail(feedId: Int, writerLevel: Int) { + private fun navigateToDetail(feedId: Int, writerId: Int) { val intent = Intent(requireContext(), DetailActivity::class.java) intent.putExtra(KEY_FEED_ID, feedId) - intent.putExtra(KEY_WRITER_LV, writerLevel) + intent.putExtra(KEY_FEED_WRITER_ID, writerId) startActivity(intent) } @@ -279,6 +279,6 @@ class WineyFeedFragment : BindingFragment(R.layout.fra private const val TAG_DELETE_DIALOG = "DELETE_DIALOG" private const val KEY_FEED_ID = "feedId" - private const val KEY_WRITER_LV = "writerLevel" + private const val KEY_FEED_WRITER_ID = "feedWriterId" } } diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt index 39ca2c96..88d6f16a 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt @@ -1,4 +1,5 @@ import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView @@ -6,14 +7,20 @@ import com.android.go.sopt.winey.databinding.ItemDetailCommentBinding import com.android.go.sopt.winey.domain.entity.Comment import com.android.go.sopt.winey.util.view.ItemDiffCallback -class CommentAdapter : ListAdapter(diffUtil) { - class CommentViewHolder( +class CommentAdapter( + private val onPopupMenuClicked: (View, Int) -> Unit +) : ListAdapter(diffUtil) { + inner class CommentViewHolder( private val binding: ItemDetailCommentBinding ) : RecyclerView.ViewHolder(binding.root) { - fun onBind(data: Comment) { + fun onBind(comment: Comment) { binding.apply { - this.data = data - executePendingBindings() + this.data = comment + + // 팝업 메뉴 버튼을 클릭하면, 해당 버튼의 뷰와 댓글 작성자의 아이디를 전달한다. + ivCommentMore.setOnClickListener { view -> + onPopupMenuClicked(view, comment.authorId) + } } } } diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt index 10a6b15f..de9b8f3d 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt @@ -2,6 +2,7 @@ package com.android.go.sopt.winey.presentation.main.feed.detail import CommentAdapter import android.os.Bundle +import android.view.View import androidx.activity.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope @@ -10,24 +11,34 @@ import com.android.go.sopt.winey.R import com.android.go.sopt.winey.databinding.ActivityDetailBinding import com.android.go.sopt.winey.domain.entity.Comment import com.android.go.sopt.winey.domain.entity.DetailFeed +import com.android.go.sopt.winey.domain.repository.DataStoreRepository import com.android.go.sopt.winey.util.binding.BindingActivity import com.android.go.sopt.winey.util.context.snackBar +import com.android.go.sopt.winey.util.context.stringOf +import com.android.go.sopt.winey.util.fragment.WineyDialogFragment import com.android.go.sopt.winey.util.view.UiState +import com.android.go.sopt.winey.util.view.WineyPopupMenu import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import timber.log.Timber +import javax.inject.Inject @AndroidEntryPoint class DetailActivity : BindingActivity(R.layout.activity_detail) { private val viewModel by viewModels() private val feedId by lazy { intent.getIntExtra(KEY_FEED_ID, 0) } - private val writerLevel by lazy { intent.getIntExtra(KEY_WRITER_LV, 0) } + private val feedWriterId by lazy { intent.getIntExtra(KEY_FEED_WRITER_ID, 0) } private var detailFeedAdapter: DetailFeedAdapter? = null + private var commentAdapter: CommentAdapter? = null private val commentEmptyAdapter by lazy { CommentEmptyAdapter() } - private val commentAdapter by lazy { CommentAdapter() } + + @Inject + lateinit var dataStoreRepository: DataStoreRepository override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -37,12 +48,122 @@ class DetailActivity : BindingActivity(R.layout.activity_ initGetFeedDetailObserver() initBackButtonClickListener() - initCommentUploadButtonClickListener() + initCommentAdapter() + initCommentCreateButtonClickListener() initPostCommentStateObserver() } - private fun initCommentUploadButtonClickListener() { - binding.tvCommentUpload.setOnClickListener { + private fun initCommentAdapter() { + commentAdapter = CommentAdapter( + onPopupMenuClicked = { view, commentAuthorId -> + //showPopupMenu(view, commentAuthorId) + showDeletePopupMenu(view) + } + ) + } + + private fun showPopupMenu(view: View, commentAuthorId: Int) { + lifecycleScope.launch { + val currentUserId = dataStoreRepository.getUserId().first() + + if (isMyFeed(currentUserId)) { + if (isMyComment(currentUserId, commentAuthorId)) { + // todo: 내 댓글 삭제 + showDeletePopupMenu(view) + } else { + // todo: 방문자 댓글 삭제/신고 + showAllPopupMenu(view) + } + } else { + if (isMyComment(currentUserId, commentAuthorId)) { + // todo: 내 댓글 삭제 + showDeletePopupMenu(view) + } else { + // todo: 다른 사람 댓글 신고 + showReportPopupMenu(view) + } + } + } + } + + private fun showDeletePopupMenu(anchorView: View) { + val deleteTitle = listOf(stringOf(R.string.popup_delete_title)) + + val popupMenu = WineyPopupMenu( + context = anchorView.context, + titles = deleteTitle + ) { _, _, _ -> + showCommentDeleteDialog() + } + + // todo: 팝업 메뉴 표시 위치 바꾸기 + popupMenu.showAsDropDown(anchorView, 0, 0) + } + + private fun showReportPopupMenu(view: View) { + val reportTitle = listOf(stringOf(R.string.popup_report_title)) + val popupMenu = + WineyPopupMenu(context = view.context, titles = reportTitle) { _, _, _ -> + showCommentReportDialog() + } + + // todo: 팝업 메뉴 표시 위치 바꾸기 + popupMenu.showAsDropDown(view) + } + + private fun showAllPopupMenu(view: View) { + val menuTitles = listOf( + stringOf(R.string.popup_delete_title), + stringOf(R.string.popup_report_title) + ) + val popupMenu = + WineyPopupMenu(context = view.context, titles = menuTitles) { _, _, position -> + when (position) { + 0 -> { + showCommentDeleteDialog() + } + + 1 -> { + showCommentReportDialog() + } + } + } + + // todo: 팝업 메뉴 표시 위치 바꾸기 + popupMenu.showAsDropDown(view) + } + + private fun showCommentDeleteDialog() { + val dialog = WineyDialogFragment( + stringOf(R.string.comment_delete_dialog_title), + stringOf(R.string.comment_delete_dialog_subtitle), + stringOf(R.string.comment_delete_dialog_negative_button), + stringOf(R.string.comment_delete_dialog_positive_button), + handleNegativeButton = {}, + handlePositiveButton = { /* todo: 댓글 삭제하기 */ } + ) + dialog.show(supportFragmentManager, TAG_COMMENT_DELETE_DIALOG) + } + + private fun showCommentReportDialog() { + val dialog = WineyDialogFragment( + stringOf(R.string.report_dialog_title), + stringOf(R.string.report_dialog_subtitle), + stringOf(R.string.report_dialog_negative_button), + stringOf(R.string.report_dialog_positive_button), + handleNegativeButton = {}, + handlePositiveButton = { /* todo: 댓글 신고하기 */ } + ) + dialog.show(supportFragmentManager, TAG_COMMENT_REPORT_DIALOG) + } + + private fun isMyFeed(currentUserId: Int?) = currentUserId == feedWriterId + + private fun isMyComment(currentUserId: Int?, commentAuthorId: Int) = + currentUserId == commentAuthorId + + private fun initCommentCreateButtonClickListener() { + binding.tvCommentCreate.setOnClickListener { val content = binding.etComment.text.toString() viewModel.postComment(feedId, content) } @@ -54,11 +175,13 @@ class DetailActivity : BindingActivity(R.layout.activity_ when (state) { is UiState.Success -> { val comment = state.data ?: return@onEach - commentAdapter.addItem(comment) + commentAdapter?.addItem(comment) } + is UiState.Failure -> { snackBar(binding.root) { state.msg } } + else -> { } } @@ -102,7 +225,7 @@ class DetailActivity : BindingActivity(R.layout.activity_ binding.rvDetail.adapter = ConcatAdapter(detailFeedAdapter, commentEmptyAdapter) } else { binding.rvDetail.adapter = ConcatAdapter(detailFeedAdapter, commentAdapter) - commentAdapter.submitList(commentList) + commentAdapter?.submitList(commentList) } } @@ -114,6 +237,9 @@ class DetailActivity : BindingActivity(R.layout.activity_ companion object { private const val KEY_FEED_ID = "feedId" - private const val KEY_WRITER_LV = "writerLevel" + private const val KEY_FEED_WRITER_ID = "feedWriterId" + + private const val TAG_COMMENT_DELETE_DIALOG = "COMMENT_DELETE_DIALOG" + private const val TAG_COMMENT_REPORT_DIALOG = "COMMENT_REPORT_DIALOG" } } diff --git a/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt b/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt index 37d6e1bb..fb1355a9 100644 --- a/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt +++ b/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt @@ -1,37 +1,63 @@ package com.android.go.sopt.winey.util.view import android.content.Context +import android.util.TypedValue import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.widget.PopupWindow import com.android.go.sopt.winey.databinding.ItemWineyPopupBinding import com.android.go.sopt.winey.databinding.LayoutWineyPopupBinding +import com.android.go.sopt.winey.util.context.drawableOf class WineyPopupMenu( private val context: Context, - private val titles: MutableList, + private val titles: List, private val menuItemClickListener: (View, String, Int) -> Unit ) : PopupWindow(context) { init { - addMenuItemViewToLayout() + isOutsideTouchable = true + isTouchable = true + inflateMenuItemsToLayout() + setUpContentView() } - private fun addMenuItemViewToLayout() { + private fun inflateMenuItemsToLayout() { val inflater = LayoutInflater.from(context) val layoutBinding = LayoutWineyPopupBinding.inflate(inflater, null, false) - val itemBinding = ItemWineyPopupBinding.inflate(inflater, null, false) + contentView = layoutBinding.root - for (i in 0 until titles.size) { + for (i in titles.indices) { + val itemBinding = ItemWineyPopupBinding.inflate(inflater, null, false) itemBinding.tvPopupTitle.text = titles[i] itemBinding.vPopupSeperator.visibility = if (i < titles.size - 1) View.VISIBLE else View.INVISIBLE - itemBinding.root.setOnClickListener { rootView -> - menuItemClickListener.invoke(rootView, titles[i], i) + val menuItemView = itemBinding.root + menuItemView.setOnClickListener { view -> + menuItemClickListener.invoke(view, titles[i], i) dismiss() } - - layoutBinding.llPopupContainer.addView(itemBinding.root) + layoutBinding.llPopupContainer.addView(menuItemView) } } + + private fun setUpContentView() { + width = getDp(context, POPUP_MENU_WIDTH) + contentView.measure( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + height = contentView.measuredHeight + setBackgroundDrawable(context.drawableOf(android.R.drawable.screen_background_light_transparent)) + } + + private fun getDp(context: Context, value: Float): Int { + val dm = context.resources.displayMetrics + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, dm).toInt() + } + + companion object { + private const val POPUP_MENU_WIDTH = 120f + } } diff --git a/app/src/main/res/drawable/shape_white_fill_5_rect.xml b/app/src/main/res/drawable/shape_white_fill_5_rect.xml new file mode 100644 index 00000000..edb6b8d5 --- /dev/null +++ b/app/src/main/res/drawable/shape_white_fill_5_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/layout/activity_detail.xml b/app/src/main/res/layout/activity_detail.xml index 4ec9c0c2..e0e8eaae 100644 --- a/app/src/main/res/layout/activity_detail.xml +++ b/app/src/main/res/layout/activity_detail.xml @@ -81,12 +81,12 @@ android:text="@={vm._comment}" android:textAppearance="@style/TextAppearance.WINEY.body2_m_15" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/tv_comment_upload" + app:layout_constraintEnd_toStartOf="@id/tv_comment_create" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + android:background="@android:color/transparent"> + android:layout_height="match_parent" + android:layout_margin="4dp" + android:background="@drawable/shape_white_fill_5_rect" + android:elevation="2dp" + android:orientation="vertical"/> - + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a87fcf5e..71f5e280 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,10 +18,16 @@ 설정하기 - 삭제하시겠습니까? - 삭제한 글은 다시 복구할 수 없습니다. - 취소 - 삭제하기 + 삭제하시겠습니까? + 삭제한 글은 다시 복구할 수 없습니다. + 취소 + 삭제하기 + + + 신고하시겠습니까? + 욕설/비하, 상업적 광고 및 판매,\n낚시/놀람/도배 글의 경우 신고할 수 있습니다. + 취소 + 신고하기 정말 게시물을 삭제하시겠어요? @@ -198,4 +204,8 @@ 등록 %d/500 450/500 + + + 삭제하기 + 신고하기 From 6271d46b6667cb3424afa83d1e11304cbf5665d5 Mon Sep 17 00:00:00 2001 From: leeeha Date: Mon, 21 Aug 2023 20:15:49 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[ui]=20#132=20=ED=8C=9D=EC=97=85=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20elevation,=20=EB=B0=B0=EA=B2=BD=EC=83=89=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../winey/presentation/main/feed/detail/DetailActivity.kt | 2 +- .../com/android/go/sopt/winey/util/view/WineyPopupMenu.kt | 3 ++- app/src/main/res/drawable/shape_transparent_fill_5_rect.xml | 6 ++++++ app/src/main/res/layout/layout_winey_popup.xml | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/drawable/shape_transparent_fill_5_rect.xml diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt index de9b8f3d..3eec49e8 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt @@ -57,7 +57,7 @@ class DetailActivity : BindingActivity(R.layout.activity_ commentAdapter = CommentAdapter( onPopupMenuClicked = { view, commentAuthorId -> //showPopupMenu(view, commentAuthorId) - showDeletePopupMenu(view) + showAllPopupMenu(view) } ) } diff --git a/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt b/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt index fb1355a9..54101492 100644 --- a/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt +++ b/app/src/main/java/com/android/go/sopt/winey/util/view/WineyPopupMenu.kt @@ -6,6 +6,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.PopupWindow +import com.android.go.sopt.winey.R import com.android.go.sopt.winey.databinding.ItemWineyPopupBinding import com.android.go.sopt.winey.databinding.LayoutWineyPopupBinding import com.android.go.sopt.winey.util.context.drawableOf @@ -49,7 +50,7 @@ class WineyPopupMenu( ViewGroup.LayoutParams.WRAP_CONTENT ) height = contentView.measuredHeight - setBackgroundDrawable(context.drawableOf(android.R.drawable.screen_background_light_transparent)) + setBackgroundDrawable(context.drawableOf(R.drawable.shape_transparent_fill_5_rect)) } private fun getDp(context: Context, value: Float): Int { diff --git a/app/src/main/res/drawable/shape_transparent_fill_5_rect.xml b/app/src/main/res/drawable/shape_transparent_fill_5_rect.xml new file mode 100644 index 00000000..ffc38ba4 --- /dev/null +++ b/app/src/main/res/drawable/shape_transparent_fill_5_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/layout/layout_winey_popup.xml b/app/src/main/res/layout/layout_winey_popup.xml index f6f7c90c..9e44daee 100644 --- a/app/src/main/res/layout/layout_winey_popup.xml +++ b/app/src/main/res/layout/layout_winey_popup.xml @@ -10,7 +10,7 @@ android:layout_height="match_parent" android:layout_margin="4dp" android:background="@drawable/shape_white_fill_5_rect" - android:elevation="2dp" + android:elevation="3dp" android:orientation="vertical"/> From 950f5404d8f0f2b40a61674e6c6298e1172f98c3 Mon Sep 17 00:00:00 2001 From: leeeha Date: Mon, 21 Aug 2023 20:26:56 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[mod]=20#132=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=ED=91=9C=EC=8B=9C=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/winey/presentation/main/feed/detail/DetailActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt index 3eec49e8..51780d88 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt @@ -2,6 +2,7 @@ package com.android.go.sopt.winey.presentation.main.feed.detail import CommentAdapter import android.os.Bundle +import android.view.Gravity import android.view.View import androidx.activity.viewModels import androidx.lifecycle.flowWithLifecycle @@ -130,7 +131,7 @@ class DetailActivity : BindingActivity(R.layout.activity_ } // todo: 팝업 메뉴 표시 위치 바꾸기 - popupMenu.showAsDropDown(view) + popupMenu.showAsDropDown(view, -65, -65, Gravity.END) } private fun showCommentDeleteDialog() { From 2391a82d9ff43f9d026777bcc882712e1971aa00 Mon Sep 17 00:00:00 2001 From: leeeha Date: Mon, 21 Aug 2023 20:36:55 +0900 Subject: [PATCH 6/8] =?UTF-8?q?[feat]=20#132=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=EC=9E=90,=20=EB=8C=93=EA=B8=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=EA=B5=AC=EB=B6=84=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=ED=8C=9D=EC=97=85=20=EB=A9=94=EB=89=B4=20=EB=9D=84=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/feed/detail/DetailActivity.kt | 75 ++++++++----------- 1 file changed, 33 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt index 51780d88..f72b4324 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt @@ -56,32 +56,31 @@ class DetailActivity : BindingActivity(R.layout.activity_ private fun initCommentAdapter() { commentAdapter = CommentAdapter( - onPopupMenuClicked = { view, commentAuthorId -> - //showPopupMenu(view, commentAuthorId) - showAllPopupMenu(view) + onPopupMenuClicked = { anchorView, commentAuthorId -> + showPopupMenu(anchorView, commentAuthorId) } ) } - private fun showPopupMenu(view: View, commentAuthorId: Int) { + private fun showPopupMenu(anchorView: View, commentAuthorId: Int) { lifecycleScope.launch { val currentUserId = dataStoreRepository.getUserId().first() if (isMyFeed(currentUserId)) { if (isMyComment(currentUserId, commentAuthorId)) { - // todo: 내 댓글 삭제 - showDeletePopupMenu(view) + // 내 댓글 삭제 + showDeletePopupMenu(anchorView) } else { - // todo: 방문자 댓글 삭제/신고 - showAllPopupMenu(view) + // 방문자 댓글 삭제/신고 + showAllPopupMenu(anchorView) } } else { if (isMyComment(currentUserId, commentAuthorId)) { - // todo: 내 댓글 삭제 - showDeletePopupMenu(view) + // 내 댓글 삭제 + showDeletePopupMenu(anchorView) } else { - // todo: 다른 사람 댓글 신고 - showReportPopupMenu(view) + // 다른 사람 댓글 신고 + showReportPopupMenu(anchorView) } } } @@ -89,49 +88,39 @@ class DetailActivity : BindingActivity(R.layout.activity_ private fun showDeletePopupMenu(anchorView: View) { val deleteTitle = listOf(stringOf(R.string.popup_delete_title)) - - val popupMenu = WineyPopupMenu( - context = anchorView.context, - titles = deleteTitle - ) { _, _, _ -> + WineyPopupMenu(context = anchorView.context, titles = deleteTitle) { _, _, _ -> showCommentDeleteDialog() + }.apply { + showCustomPosition(anchorView) } - - // todo: 팝업 메뉴 표시 위치 바꾸기 - popupMenu.showAsDropDown(anchorView, 0, 0) } - private fun showReportPopupMenu(view: View) { + private fun showReportPopupMenu(anchorView: View) { val reportTitle = listOf(stringOf(R.string.popup_report_title)) - val popupMenu = - WineyPopupMenu(context = view.context, titles = reportTitle) { _, _, _ -> - showCommentReportDialog() - } - - // todo: 팝업 메뉴 표시 위치 바꾸기 - popupMenu.showAsDropDown(view) + WineyPopupMenu(context = anchorView.context, titles = reportTitle) { _, _, _ -> + showCommentReportDialog() + }.apply { + showCustomPosition(anchorView) + } } - private fun showAllPopupMenu(view: View) { + private fun showAllPopupMenu(anchorView: View) { val menuTitles = listOf( stringOf(R.string.popup_delete_title), stringOf(R.string.popup_report_title) ) - val popupMenu = - WineyPopupMenu(context = view.context, titles = menuTitles) { _, _, position -> - when (position) { - 0 -> { - showCommentDeleteDialog() - } - - 1 -> { - showCommentReportDialog() - } - } + WineyPopupMenu(context = anchorView.context, titles = menuTitles) { _, _, position -> + when (position) { + 0 -> showCommentDeleteDialog() + 1 -> showCommentReportDialog() } + }.apply { + showCustomPosition(anchorView) + } + } - // todo: 팝업 메뉴 표시 위치 바꾸기 - popupMenu.showAsDropDown(view, -65, -65, Gravity.END) + private fun WineyPopupMenu.showCustomPosition(anchorView: View) { + showAsDropDown(anchorView, -POPUP_MENU_OFFSET, -POPUP_MENU_OFFSET, Gravity.END) } private fun showCommentDeleteDialog() { @@ -242,5 +231,7 @@ class DetailActivity : BindingActivity(R.layout.activity_ private const val TAG_COMMENT_DELETE_DIALOG = "COMMENT_DELETE_DIALOG" private const val TAG_COMMENT_REPORT_DIALOG = "COMMENT_REPORT_DIALOG" + + private const val POPUP_MENU_OFFSET = 65 } } From 092707d29084b6853bc4aa3ae456ae86dba59615 Mon Sep 17 00:00:00 2001 From: leeeha Date: Mon, 21 Aug 2023 22:00:19 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[feat]=20#132=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=9B=84=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=99=80=20=EB=8C=93=EA=B8=80=20=EC=B4=9D=20=EA=B0=9C=EC=88=98?= =?UTF-8?q?=20=EA=B0=B1=EC=8B=A0=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/response/ResponsePostCommentDto.kt | 11 +++++---- .../go/sopt/winey/domain/entity/DetailFeed.kt | 2 +- .../main/feed/detail/CommentAdapter.kt | 4 ++-- .../main/feed/detail/DetailActivity.kt | 18 ++++++++++----- .../main/feed/detail/DetailFeedAdapter.kt | 23 +++++++++++-------- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponsePostCommentDto.kt b/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponsePostCommentDto.kt index 134ee0fc..a0018f36 100644 --- a/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponsePostCommentDto.kt +++ b/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponsePostCommentDto.kt @@ -1,6 +1,7 @@ package com.android.go.sopt.winey.data.model.remote.response import com.android.go.sopt.winey.domain.entity.Comment +import kotlinx.datetime.LocalDateTime import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -8,16 +9,16 @@ import kotlinx.serialization.Serializable data class ResponsePostCommentDto( @SerialName("commentId") val commentId: Long, - @SerialName("commentCounter") - val commentCounter: Long, + @SerialName("authorId") + val authorId: Int, @SerialName("author") val author: String, @SerialName("content") val content: String, - @SerialName("authorId") - val authorId: Int, @SerialName("authorLevel") - val authorLevel: Int + val authorLevel: Int, + @SerialName("createdAt") + val createdAt: LocalDateTime ) { fun toComment() = Comment( commentId = this.commentId, diff --git a/app/src/main/java/com/android/go/sopt/winey/domain/entity/DetailFeed.kt b/app/src/main/java/com/android/go/sopt/winey/domain/entity/DetailFeed.kt index 8bfdb966..11d01c65 100644 --- a/app/src/main/java/com/android/go/sopt/winey/domain/entity/DetailFeed.kt +++ b/app/src/main/java/com/android/go/sopt/winey/domain/entity/DetailFeed.kt @@ -10,7 +10,7 @@ data class DetailFeed( val nickName: String, val userId: Int, val writerLevel: Int, - val comments: Long, + var comments: Long, val timeAgo: String, val commentList: List ) diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt index 88d6f16a..c57a8cc5 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/CommentAdapter.kt @@ -38,13 +38,13 @@ class CommentAdapter( holder.onBind(getItem(position)) } - fun addItem(item: Comment) { + fun addItem(item: Comment): Int { val newList = currentList.toMutableList() newList.add(item) submitList(newList) + return newList.size } - // todo: 어댑터에서 아이템 삭제한 뒤에, 실제로 서버통신도 해줘야 다음에 GET 할 때 반영된다! fun deleteItem(position: Int) { val newList = currentList.toMutableList() newList.removeAt(position) diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt index f72b4324..eef58ae2 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailActivity.kt @@ -34,8 +34,12 @@ class DetailActivity : BindingActivity(R.layout.activity_ private val feedId by lazy { intent.getIntExtra(KEY_FEED_ID, 0) } private val feedWriterId by lazy { intent.getIntExtra(KEY_FEED_WRITER_ID, 0) } - private var detailFeedAdapter: DetailFeedAdapter? = null - private var commentAdapter: CommentAdapter? = null + private var _detailFeedAdapter: DetailFeedAdapter? = null + private val detailFeedAdapter get() = requireNotNull(_detailFeedAdapter) + + private var _commentAdapter: CommentAdapter? = null + private val commentAdapter get() = requireNotNull(_commentAdapter) + private val commentEmptyAdapter by lazy { CommentEmptyAdapter() } @Inject @@ -55,7 +59,7 @@ class DetailActivity : BindingActivity(R.layout.activity_ } private fun initCommentAdapter() { - commentAdapter = CommentAdapter( + _commentAdapter = CommentAdapter( onPopupMenuClicked = { anchorView, commentAuthorId -> showPopupMenu(anchorView, commentAuthorId) } @@ -165,7 +169,9 @@ class DetailActivity : BindingActivity(R.layout.activity_ when (state) { is UiState.Success -> { val comment = state.data ?: return@onEach - commentAdapter?.addItem(comment) + val commentNumber = commentAdapter.addItem(comment) + detailFeedAdapter.updateCommentNumber(commentNumber.toLong()) + binding.etComment.text.clear() } is UiState.Failure -> { @@ -202,7 +208,7 @@ class DetailActivity : BindingActivity(R.layout.activity_ Timber.e("DETAIL FEED IS NULL") return } - detailFeedAdapter = DetailFeedAdapter(detailFeed) + _detailFeedAdapter = DetailFeedAdapter(detailFeed) } private fun switchCommentContainer(commentList: List?) { @@ -215,7 +221,7 @@ class DetailActivity : BindingActivity(R.layout.activity_ binding.rvDetail.adapter = ConcatAdapter(detailFeedAdapter, commentEmptyAdapter) } else { binding.rvDetail.adapter = ConcatAdapter(detailFeedAdapter, commentAdapter) - commentAdapter?.submitList(commentList) + commentAdapter.submitList(commentList) } } diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailFeedAdapter.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailFeedAdapter.kt index bc2c45e4..163b3587 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailFeedAdapter.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/detail/DetailFeedAdapter.kt @@ -8,8 +8,14 @@ import com.android.go.sopt.winey.domain.entity.DetailFeed class DetailFeedAdapter( private val detailFeed: DetailFeed -) : - RecyclerView.Adapter() { +) : RecyclerView.Adapter() { + class DetailFeedViewHolder( + private val binding: ItemDetailFeedBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: DetailFeed) { + binding.data = data + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailFeedViewHolder { return DetailFeedViewHolder( @@ -21,18 +27,15 @@ class DetailFeedAdapter( ) } + override fun getItemCount(): Int = FEED_ITEM_COUNT + override fun onBindViewHolder(holder: DetailFeedViewHolder, position: Int) { holder.bind(detailFeed) } - override fun getItemCount(): Int = FEED_ITEM_COUNT - - class DetailFeedViewHolder( - private val binding: ItemDetailFeedBinding - ) : RecyclerView.ViewHolder(binding.root) { - fun bind(data: DetailFeed) { - binding.data = data - } + fun updateCommentNumber(comments: Long) { + detailFeed.comments = comments + notifyItemChanged(0) } companion object { From 06e8df32d2664d7982e5e9e35b361befac74052b Mon Sep 17 00:00:00 2001 From: leeeha Date: Mon, 21 Aug 2023 22:09:51 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[fix]=20#132=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=9B=84=20=EC=9C=84=EB=8B=88=ED=94=BC?= =?UTF-8?q?=EB=93=9C=20=EB=8B=A4=EC=8B=9C=20=EB=8F=8C=EC=95=84=EC=98=A4?= =?UTF-8?q?=EB=A9=B4=20=EB=8C=93=EA=B8=80=20=EC=88=98=EA=B0=80=20=EA=B0=B1?= =?UTF-8?q?=EC=8B=A0=EB=90=98=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/go/sopt/winey/presentation/main/MainActivity.kt | 6 +++--- .../sopt/winey/presentation/main/feed/WineyFeedFragment.kt | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/MainActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/MainActivity.kt index dda3700e..870e6492 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/MainActivity.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/MainActivity.kt @@ -25,14 +25,14 @@ import kotlinx.coroutines.flow.onEach @AndroidEntryPoint class MainActivity : BindingActivity(R.layout.activity_main) { - private val viewModel by viewModels() + private val mainViewModel by viewModels() private val isUploadSuccess by lazy { intent.extras?.getBoolean(EXTRA_UPLOAD_KEY, false) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 위니피드, 마이페이지 프래그먼트에서 getUserState 관찰 - viewModel.getUser() + mainViewModel.getUser() initFragment() initBnvItemSelectedListener() @@ -84,7 +84,7 @@ class MainActivity : BindingActivity(R.layout.activity_main } private fun setupLogoutState() { - viewModel.logoutState.flowWithLifecycle(lifecycle).onEach { state -> + mainViewModel.logoutState.flowWithLifecycle(lifecycle).onEach { state -> when (state) { is UiState.Loading -> { } diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt index 64adf881..b9742eee 100644 --- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt +++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt @@ -71,6 +71,12 @@ class WineyFeedFragment : BindingFragment(R.layout.fra initNotificationButtonClickListener() } + // 상세 피드 갔다가 다시 돌아오면 갱신된 데이터가 보이도록 + override fun onStart() { + super.onStart() + viewModel.getWineyFeed() + } + private fun initAdapter() { wineyFeedHeaderAdapter = WineyFeedHeaderAdapter() wineyFeedLoadAdapter = WineyFeedLoadAdapter()