diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt new file mode 100644 index 00000000..4fd67a17 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt @@ -0,0 +1,206 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.fantalk + +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.core.view.isVisible +import androidx.recyclerview.widget.LinearLayoutManager +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.base.BaseFragment +import kr.co.vividnext.sodalive.databinding.FragmentCreatorChannelFantalkBinding +import kr.co.vividnext.sodalive.extensions.moneyFormat +import kr.co.vividnext.sodalive.report.CheersReportDialog +import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkRightAction +import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkUiModel +import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.ui.CreatorChannelFanTalkAdapter +import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.ui.CreatorChannelFanTalkMorePopup +import org.koin.androidx.viewmodel.ext.android.viewModel + +class CreatorChannelFanTalkFragment : BaseFragment( + FragmentCreatorChannelFantalkBinding::inflate +) { + + private val viewModel: CreatorChannelFanTalkViewModel by viewModel() + private val fanTalkAdapter = CreatorChannelFanTalkAdapter( + onOwnerMoreClick = ::showOwnerMorePopup, + onReportClick = ::showReportDialog + ) + private var morePopup: CreatorChannelFanTalkMorePopup? = null + private var lastContentLayoutKey: CreatorChannelFanTalkContentLayoutKey? = null + private val creatorId: Long by lazy { arguments?.getLong(ARG_CREATOR_ID) ?: 0L } + private val host: Host + get() = requireActivity() as Host + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + bindLoading() + setupFanTalkList() + setupClickListeners() + observeViewModel() + } + + override fun onDestroyView() { + morePopup?.dismiss() + morePopup = null + lastContentLayoutKey = null + binding.rvCreatorChannelFantalk.adapter = null + super.onDestroyView() + } + + fun onCreatorChannelFanTalkTabSelected() { + if (creatorId > 0L) { + viewModel.loadFanTalks(creatorId, isOwner = host.isCreatorChannelOwner()) + } + } + + fun onCreatorChannelFanTalkScrolledToBottom() { + viewModel.loadMore() + } + + fun onCreatorChannelFanTalkRefreshRequested() { + viewModel.refreshFanTalks() + } + + fun onCreatorChannelFanTalkViewportHeightChanged(minHeight: Int) { + binding.root.minimumHeight = minHeight + } + + fun onCreatorChannelFanTalkDeleteConfirmed(fanTalkId: Long) { + viewModel.deleteFanTalk(fanTalkId) + } + + private fun setupFanTalkList() = with(binding.rvCreatorChannelFantalk) { + layoutManager = LinearLayoutManager(requireContext()) + adapter = fanTalkAdapter + } + + private fun setupClickListeners() = with(binding) { + btnCreatorChannelFantalkRetry.setOnClickListener { + viewModel.retryFanTalks() + } + btnCreatorChannelFantalkWrite.setOnClickListener { } + layoutCreatorChannelFantalkEmptyWriteButton.setOnClickListener { } + } + + private fun observeViewModel() { + viewModel.fanTalkStateLiveData.observe(viewLifecycleOwner) { state -> + when (state) { + CreatorChannelFanTalkUiState.Loading -> bindLoading() + is CreatorChannelFanTalkUiState.Empty -> bindEmpty() + is CreatorChannelFanTalkUiState.Error -> bindError(state) + is CreatorChannelFanTalkUiState.Content -> bindContent(state) + } + } + } + + private fun bindLoading() = with(binding) { + lastContentLayoutKey = null + layoutCreatorChannelFantalkCountBar.isVisible = false + rvCreatorChannelFantalk.isVisible = false + layoutCreatorChannelFantalkEmpty.isVisible = false + tvCreatorChannelFantalkErrorMessage.isVisible = false + btnCreatorChannelFantalkRetry.isVisible = false + btnCreatorChannelFantalkWrite.isVisible = false + } + + private fun bindEmpty() = with(binding) { + lastContentLayoutKey = null + layoutCreatorChannelFantalkCountBar.isVisible = false + rvCreatorChannelFantalk.isVisible = false + layoutCreatorChannelFantalkEmpty.isVisible = true + tvCreatorChannelFantalkErrorMessage.isVisible = false + btnCreatorChannelFantalkRetry.isVisible = false + btnCreatorChannelFantalkWrite.isVisible = false + host.onCreatorChannelFanTalkContentChanged() + } + + private fun bindError(state: CreatorChannelFanTalkUiState.Error) = with(binding) { + lastContentLayoutKey = null + layoutCreatorChannelFantalkCountBar.isVisible = false + rvCreatorChannelFantalk.isVisible = false + layoutCreatorChannelFantalkEmpty.isVisible = false + tvCreatorChannelFantalkErrorMessage.isVisible = true + tvCreatorChannelFantalkErrorMessage.text = state.message ?: getString(R.string.creator_channel_fantalk_error_message) + btnCreatorChannelFantalkRetry.isVisible = true + btnCreatorChannelFantalkWrite.isVisible = false + host.onCreatorChannelFanTalkContentChanged() + } + + private fun bindContent(state: CreatorChannelFanTalkUiState.Content) = with(binding) { + layoutCreatorChannelFantalkCountBar.isVisible = true + tvCreatorChannelFantalkTotalCount.text = state.fanTalkCount.moneyFormat() + rvCreatorChannelFantalk.isVisible = true + fanTalkAdapter.submitItems(state.fanTalks) + layoutCreatorChannelFantalkEmpty.isVisible = false + tvCreatorChannelFantalkErrorMessage.isVisible = false + btnCreatorChannelFantalkRetry.isVisible = false + btnCreatorChannelFantalkWrite.isVisible = true + notifyContentChangedIfLayoutChanged(state) + state.paginationErrorMessage?.let { + Toast.makeText(requireContext(), it, Toast.LENGTH_SHORT).show() + viewModel.consumePaginationErrorMessage() + } + state.actionToastMessage?.let { + Toast.makeText(requireContext(), it, Toast.LENGTH_SHORT).show() + viewModel.consumeActionToastMessage() + } + } + + private fun notifyContentChangedIfLayoutChanged(state: CreatorChannelFanTalkUiState.Content) { + val contentLayoutKey = state.toContentLayoutKey() + if (contentLayoutKey == lastContentLayoutKey) return + + lastContentLayoutKey = contentLayoutKey + host.onCreatorChannelFanTalkContentChanged() + } + + private fun showReportDialog(item: CreatorChannelFanTalkUiModel) { + CheersReportDialog(requireActivity(), layoutInflater) { reason -> + if (reason.isBlank()) { + showToast(getString(R.string.screen_user_profile_fantalk_report_reason_required)) + } else { + viewModel.reportFanTalk(item.fanTalkId, reason) + } + }.show(screenWidth) + } + + private fun showOwnerMorePopup(anchor: View, item: CreatorChannelFanTalkUiModel) { + val ownerMore = item.rightAction as? CreatorChannelFanTalkRightAction.OwnerMore ?: return + morePopup?.dismiss() + morePopup = CreatorChannelFanTalkMorePopup( + anchor = anchor, + fanTalkId = item.fanTalkId, + showEdit = ownerMore.showEdit, + showDelete = ownerMore.showDelete, + onDeleteClick = host::onCreatorChannelFanTalkDeleteClicked + ).also { it.show() } + } + + interface Host { + fun isCreatorChannelOwner(): Boolean + fun onCreatorChannelFanTalkContentChanged() + fun onCreatorChannelFanTalkDeleteClicked(fanTalkId: Long) + } + + companion object { + private const val ARG_CREATOR_ID: String = "arg_creator_id" + + fun newInstance(creatorId: Long): CreatorChannelFanTalkFragment { + return CreatorChannelFanTalkFragment().apply { + arguments = Bundle().apply { putLong(ARG_CREATOR_ID, creatorId) } + } + } + } +} + +private data class CreatorChannelFanTalkContentLayoutKey( + val fanTalkCount: Int, + val fanTalkIds: List +) + +private fun CreatorChannelFanTalkUiState.Content.toContentLayoutKey(): CreatorChannelFanTalkContentLayoutKey { + return CreatorChannelFanTalkContentLayoutKey( + fanTalkCount = fanTalkCount, + fanTalkIds = fanTalks.map { it.fanTalkId } + ) +} diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index b50f9e2d..83acc609 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -330,6 +330,13 @@ %1$s · Total %2$d Be the first\nto cheer them on! Leave support + No cheers yet.\nBe the first to cheer for the creator! + Could not load FanTalk. + Retry + All + Report + Edit + Delete Debut %1$s (%2$s) Total lives diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 27373042..02f073c3 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -330,6 +330,13 @@ %1$s · 全体 %2$d 最初の応援を\n待っています! 応援を残す + まだ応援がありません。\n最初にクリエイターを応援してみましょう! + ファントークを読み込めませんでした。 + 再試行 + 全体 + 通報 + 編集する + 削除する デビュー %1$s(%2$s) ライブ総配信数 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index db992e08..7cc954d5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -329,6 +329,13 @@ %1$s · 전체 %2$d 당신의 첫 응원을\n기다리고 있어요! 응원 남기기 + 아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요! + 팬톡을 불러오지 못했습니다. + 다시 시도 + 전체 + 신고 + 수정하기 + 삭제하기 데뷔 %1$s(%2$s) 라이브 총 진행 수