Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 상세 피드 / 피드 좋아요, 삭제, 신고 구현 #140

Merged
merged 12 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import kotlinx.coroutines.flow.onEach
class MainActivity : BindingActivity<ActivityMainBinding>(R.layout.activity_main) {
private val mainViewModel by viewModels<MainViewModel>()
private val isUploadSuccess by lazy { intent.extras?.getBoolean(EXTRA_UPLOAD_KEY, false) }

private val isDeleteSuccess by lazy { intent.extras?.getBoolean(EXTRA_DELETE_KEY, false) }
private val isReportSuccess by lazy { intent.extras?.getBoolean(EXTRA_REPORT_KEY, false) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand All @@ -39,7 +40,7 @@ class MainActivity : BindingActivity<ActivityMainBinding>(R.layout.activity_main
syncBottomNavigationSelection()

setupLogoutState()
showUploadSuccessSnackbar()
showSuccessSnackBar()
}

private fun initFragment() {
Expand All @@ -51,10 +52,16 @@ class MainActivity : BindingActivity<ActivityMainBinding>(R.layout.activity_main
}
}

private fun showUploadSuccessSnackbar() {
private fun showSuccessSnackBar() {
if (isUploadSuccess != null && isUploadSuccess == true) {
wineySnackbar(binding.root, true, stringOf(R.string.snackbar_upload_success))
}
if (isDeleteSuccess != null && isDeleteSuccess == true) {
wineySnackbar(binding.root, true, stringOf(R.string.snackbar_feed_delete_success))
}
if (isReportSuccess != null && isReportSuccess == true) {
wineySnackbar(binding.root, true, stringOf(R.string.snackbar_report_success))
}
}

private fun initBnvItemSelectedListener() {
Expand Down Expand Up @@ -119,5 +126,7 @@ class MainActivity : BindingActivity<ActivityMainBinding>(R.layout.activity_main

companion object {
private const val EXTRA_UPLOAD_KEY = "upload"
private const val EXTRA_DELETE_KEY = "delete"
private const val EXTRA_REPORT_KEY = "report"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import com.android.go.sopt.winey.util.view.setOnSingleClickListener
class WineyFeedAdapter(
private val likeButtonClick: (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
) : PagingDataAdapter<WineyFeed, WineyFeedAdapter.WineyFeedViewHolder>(diffUtil) {
private val currentData: ItemSnapshotList<WineyFeed>
get() = snapshot()
Expand Down Expand Up @@ -72,24 +72,6 @@ class WineyFeedAdapter(
holder.onBind(getItem(position))
}

fun updateLikeStatus(feedId: Int, isLiked: Boolean) {
currentData.let { data ->
val index = data.indexOfFirst { it?.feedId == feedId }
if (index == -1) {
return
}
data[index]?.let { item ->
item.isLiked = isLiked
if (isLiked) {
item.likes++
} else {
item.likes--
}
notifyItemChanged(index)
}
}
}

companion object {
private val diffUtil = ItemDiffCallback<WineyFeed>(
onItemsTheSame = { old, new -> old.feedId == new.feedId },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.android.go.sopt.winey.presentation.main.feed

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
Expand Down Expand Up @@ -49,7 +50,8 @@ import timber.log.Timber
import javax.inject.Inject

@AndroidEntryPoint
class WineyFeedFragment : BindingFragment<FragmentWineyFeedBinding>(R.layout.fragment_winey_feed) {
class WineyFeedFragment :
BindingFragment<FragmentWineyFeedBinding>(R.layout.fragment_winey_feed) {
private val viewModel by viewModels<WineyFeedViewModel>()
private val mainViewModel by activityViewModels<MainViewModel>()
private lateinit var wineyFeedAdapter: WineyFeedAdapter
Expand All @@ -61,6 +63,7 @@ class WineyFeedFragment : BindingFragment<FragmentWineyFeedBinding>(R.layout.fra

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("Fragment Lifecycle", "onViewCreated 호출됨")
initAdapter()
setSwipeRefreshListener()
initFabClickListener()
Expand Down Expand Up @@ -105,8 +108,10 @@ class WineyFeedFragment : BindingFragment<FragmentWineyFeedBinding>(R.layout.fra
true
)

val menuDelete = popupView.findViewById<TextView>(R.id.tv_popup_delete)
val menuReport = popupView.findViewById<TextView>(R.id.tv_popup_report)
val menuDelete =
popupView.findViewById<TextView>(R.id.tv_popup_delete)
val menuReport =
popupView.findViewById<TextView>(R.id.tv_popup_report)

if (wineyFeed.userId == runBlocking { dataStoreRepository.getUserId().first() }) {
menuReport.isVisible = false
Expand Down Expand Up @@ -188,10 +193,7 @@ class WineyFeedFragment : BindingFragment<FragmentWineyFeedBinding>(R.layout.fra
when (state) {
is UiState.Success -> {
initGetFeedStateObserver()
wineyFeedAdapter.updateLikeStatus(
state.data.data.feedId,
state.data.data.isLiked
)
wineyFeedAdapter.refresh()
}

is UiState.Failure -> {
Expand Down Expand Up @@ -256,7 +258,8 @@ class WineyFeedFragment : BindingFragment<FragmentWineyFeedBinding>(R.layout.fra
parentFragmentManager.commit {
replace<T>(R.id.fcv_main, T::class.simpleName)
}
val bottomNav: BottomNavigationView = requireActivity().findViewById(R.id.bnv_main)
val bottomNav: BottomNavigationView =
requireActivity().findViewById(R.id.bnv_main)
bottomNav.selectedItemId = R.id.menu_mypage
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.android.go.sopt.winey.presentation.main.feed.detail

import CommentAdapter
import android.content.Intent
import android.os.Bundle
import android.view.Gravity
import android.view.View
Expand All @@ -13,9 +14,11 @@ 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.presentation.main.MainActivity
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.context.wineySnackbar
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
Expand Down Expand Up @@ -52,6 +55,7 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
viewModel.getFeedDetail(feedId)
initGetFeedDetailObserver()
initBackButtonClickListener()
initPostLikeStateObserver()

initCommentAdapter()
initCommentCreateButtonClickListener()
Expand All @@ -61,27 +65,44 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
private fun initCommentAdapter() {
_commentAdapter = CommentAdapter(
onPopupMenuClicked = { anchorView, commentAuthorId ->
showPopupMenu(anchorView, commentAuthorId)
showCommentPopupMenu(anchorView, commentAuthorId)
}
)
}

private fun showPopupMenu(anchorView: View, commentAuthorId: Int) {
private fun initDetailFeedAdapter(detailFeed: DetailFeed?) {
if (detailFeed == null) {
Timber.e("DETAIL FEED IS NULL")
return
}
_detailFeedAdapter =
DetailFeedAdapter(
detailFeed,
onLikeButtonClicked = { feedId, isLiked ->
viewModel.likeFeed(feedId, isLiked)
},
onPopupMenuClicked = { anchorView ->
showFeedPopupMenu(anchorView)
}
)
}

private fun showCommentPopupMenu(anchorView: View, commentAuthorId: Int) {
lifecycleScope.launch {
val currentUserId = dataStoreRepository.getUserId().first()

if (isMyFeed(currentUserId)) {
if (isMyComment(currentUserId, commentAuthorId)) {
// 내 댓글 삭제
showDeletePopupMenu(anchorView)
showCommentDeletePopupMenu(anchorView)
} else {
// 방문자 댓글 삭제/신고
showAllPopupMenu(anchorView)
}
} else {
if (isMyComment(currentUserId, commentAuthorId)) {
// 내 댓글 삭제
showDeletePopupMenu(anchorView)
showCommentDeletePopupMenu(anchorView)
} else {
// 다른 사람 댓글 신고
showReportPopupMenu(anchorView)
Expand All @@ -90,7 +111,18 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
}
}

private fun showDeletePopupMenu(anchorView: View) {
private fun showFeedPopupMenu(anchorView: View) {
lifecycleScope.launch {
val currentUserId = dataStoreRepository.getUserId().first()
if (isMyFeed(currentUserId)) {
showFeedDeletePopupMenu(anchorView)
} else {
showReportPopupMenu(anchorView)
}
}
}

private fun showCommentDeletePopupMenu(anchorView: View) {
val deleteTitle = listOf(stringOf(R.string.popup_delete_title))
WineyPopupMenu(context = anchorView.context, titles = deleteTitle) { _, _, _ ->
showCommentDeleteDialog()
Expand All @@ -99,6 +131,15 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
}
}

private fun showFeedDeletePopupMenu(anchorView: View) {
val deleteTitle = listOf(stringOf(R.string.popup_delete_title))
WineyPopupMenu(context = anchorView.context, titles = deleteTitle) { _, _, _ ->
showFeedDeleteDialog()
}.apply {
showCustomPosition(anchorView)
}
}

private fun showReportPopupMenu(anchorView: View) {
val reportTitle = listOf(stringOf(R.string.popup_report_title))
WineyPopupMenu(context = anchorView.context, titles = reportTitle) { _, _, _ ->
Expand Down Expand Up @@ -139,14 +180,27 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
dialog.show(supportFragmentManager, TAG_COMMENT_DELETE_DIALOG)
}

private fun showFeedDeleteDialog() {
val dialog = WineyDialogFragment(
stringOf(R.string.feed_delete_dialog_title),
stringOf(R.string.feed_delete_dialog_subtitle),
stringOf(R.string.comment_delete_dialog_negative_button),
stringOf(R.string.comment_delete_dialog_positive_button),
handleNegativeButton = {},
handlePositiveButton = { viewModel.deleteFeed(feedId) }
)
dialog.show(supportFragmentManager, TAG_COMMENT_DELETE_DIALOG)
initDeleteFeedStateObserver()
}

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: 댓글 신고하기 */ }
handlePositiveButton = { navigateToMain(EXTRA_REPORT_KEY) }
)
dialog.show(supportFragmentManager, TAG_COMMENT_REPORT_DIALOG)
}
Expand Down Expand Up @@ -178,8 +232,7 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
snackBar(binding.root) { state.msg }
}

else -> {
}
else -> Timber.tag("failure").e(MSG_DETAIL_ERROR)
}
}.launchIn(lifecycleScope)
}
Expand All @@ -197,18 +250,25 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
snackBar(binding.root) { state.msg }
}

else -> {
}
else -> Timber.tag("failure").e(MSG_DETAIL_ERROR)
}
}.launchIn(lifecycleScope)
}

private fun initDetailFeedAdapter(detailFeed: DetailFeed?) {
if (detailFeed == null) {
Timber.e("DETAIL FEED IS NULL")
return
}
_detailFeedAdapter = DetailFeedAdapter(detailFeed)
private fun initDeleteFeedStateObserver() {
viewModel.deleteFeedDetailState.flowWithLifecycle(lifecycle).onEach { state ->
when (state) {
is UiState.Success -> {
navigateToMain(EXTRA_DELETE_KEY)
}

is UiState.Failure -> {
wineySnackbar(binding.root, false, stringOf(R.string.snackbar_delete_fail))
}

else -> Timber.tag("failure").e(MSG_DETAIL_ERROR)
}
}.launchIn(lifecycleScope)
}

private fun switchCommentContainer(commentList: List<Comment>?) {
Expand All @@ -231,6 +291,30 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
}
}

private fun initPostLikeStateObserver() {
viewModel.postFeedDetailLikeState.flowWithLifecycle(lifecycle).onEach { state ->
when (state) {
is UiState.Success -> {
viewModel.getFeedDetail(feedId)
}

is UiState.Failure -> {
snackBar(binding.root) { state.msg }
}

else -> Timber.tag("failure").e(MSG_DETAIL_ERROR)
}
}.launchIn(lifecycleScope)
}

private fun navigateToMain(extraKey: String) {
Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(extraKey, true)
startActivity(this)
}
}

companion object {
private const val KEY_FEED_ID = "feedId"
private const val KEY_FEED_WRITER_ID = "feedWriterId"
Expand All @@ -239,5 +323,10 @@ class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_
private const val TAG_COMMENT_REPORT_DIALOG = "COMMENT_REPORT_DIALOG"

private const val POPUP_MENU_OFFSET = 65

private const val MSG_DETAIL_ERROR = "ERROR"

private const val EXTRA_DELETE_KEY = "delete"
private const val EXTRA_REPORT_KEY = "report"
}
}
Loading