From 49c868eececc7b8254c59e845a19b21393b3fbf6 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Sat, 2 Nov 2024 18:04:49 +0900 Subject: [PATCH 1/5] [feat] #182 delete link name limit --- .../components/bottomsheet/BottomSheetType.kt | 6 +++++ .../bottomsheet/LinkMindBottomSheet.kt | 22 +++++++++++++++---- .../java/org/sopt/clip/clip/ClipFragment.kt | 2 ++ .../sopt/clip/clipedit/ClipEditFragment.kt | 2 ++ .../sopt/clip/cliplink/ClipLinkFragment.kt | 3 ++- .../main/java/org/sopt/home/HomeFragment.kt | 4 +++- .../SaveLinkSetClipFragment.kt | 4 +++- 7 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 core/designsystem/src/main/java/designsystem/components/bottomsheet/BottomSheetType.kt diff --git a/core/designsystem/src/main/java/designsystem/components/bottomsheet/BottomSheetType.kt b/core/designsystem/src/main/java/designsystem/components/bottomsheet/BottomSheetType.kt new file mode 100644 index 00000000..05347b9f --- /dev/null +++ b/core/designsystem/src/main/java/designsystem/components/bottomsheet/BottomSheetType.kt @@ -0,0 +1,6 @@ +package designsystem.components.bottomsheet + +enum class BottomSheetType { + LINK, + CLIP, +} diff --git a/core/designsystem/src/main/java/designsystem/components/bottomsheet/LinkMindBottomSheet.kt b/core/designsystem/src/main/java/designsystem/components/bottomsheet/LinkMindBottomSheet.kt index 002e4a01..b03d99d3 100644 --- a/core/designsystem/src/main/java/designsystem/components/bottomsheet/LinkMindBottomSheet.kt +++ b/core/designsystem/src/main/java/designsystem/components/bottomsheet/LinkMindBottomSheet.kt @@ -30,10 +30,12 @@ class LinkMindBottomSheet(context: Context) { window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) binding.etvBottomSheet.editText.requestFocus() } + } + fun setBottomSheetType(bottomSheetType: BottomSheetType) { binding.etvBottomSheet.apply { throttleAfterTextChanged { - handleTextChange() + handleTextChange(bottomSheetType) } onClickTextClear { @@ -43,6 +45,7 @@ class LinkMindBottomSheet(context: Context) { } } } + fun setBottomSheetHint(@StringRes textId: Int) { binding.etvBottomSheet.editText.setHint(textId) } @@ -62,8 +65,8 @@ class LinkMindBottomSheet(context: Context) { } } - fun handleTextChange() { - val isError = showErrorMsg() + private fun handleTextChange(bottomSheetType: BottomSheetType) { + val isError = showErrorMsg(bottomSheetType) binding.apply { tvBottomSheetErrorText.isVisible = isError if (isError) binding.etvBottomSheet.editText.filters = arrayOf(InputFilter.LengthFilter(16)) @@ -76,7 +79,18 @@ class LinkMindBottomSheet(context: Context) { fun setTitle(@StringRes textId: Int) { binding.tvBottomSheetTitle.setText(textId) } - fun showErrorMsg(): Boolean = binding.etvBottomSheet.editText.text.length > 15 || binding.etvBottomSheet.editText.text.isEmpty() + + fun showErrorMsg(bottomSheetType: BottomSheetType): Boolean { + return when (bottomSheetType) { + BottomSheetType.LINK -> { + binding.etvBottomSheet.editText.text.isEmpty() + } + + BottomSheetType.CLIP -> { + binding.etvBottomSheet.editText.text.length > 15 || binding.etvBottomSheet.editText.text.isEmpty() + } + } + } fun setErroMsg(@StringRes textId: Int) { binding.tvBottomSheetErrorText.setText(textId) diff --git a/feature/clip/src/main/java/org/sopt/clip/clip/ClipFragment.kt b/feature/clip/src/main/java/org/sopt/clip/clip/ClipFragment.kt index 7d4f5b47..6ac5bc02 100644 --- a/feature/clip/src/main/java/org/sopt/clip/clip/ClipFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/clip/ClipFragment.kt @@ -6,6 +6,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint +import designsystem.components.bottomsheet.BottomSheetType import designsystem.components.bottomsheet.LinkMindBottomSheet import designsystem.components.toast.linkMindSnackBar import kotlinx.coroutines.flow.launchIn @@ -115,6 +116,7 @@ class ClipFragment : BindingFragment({ FragmentClipBinding. val addClipBottomSheet = LinkMindBottomSheet(requireContext()) addClipBottomSheet.show() addClipBottomSheet.apply { + setBottomSheetType(BottomSheetType.CLIP) setBottomSheetHint(org.sopt.mainfeature.R.string.clip_new_clip_info) setTitle(org.sopt.mainfeature.R.string.clip_add_clip) setErroMsg(org.sopt.mainfeature.R.string.error_clip_length) diff --git a/feature/clip/src/main/java/org/sopt/clip/clipedit/ClipEditFragment.kt b/feature/clip/src/main/java/org/sopt/clip/clipedit/ClipEditFragment.kt index 66591c37..df9e19c3 100644 --- a/feature/clip/src/main/java/org/sopt/clip/clipedit/ClipEditFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/clipedit/ClipEditFragment.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.flowWithLifecycle import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.ItemTouchHelper import dagger.hilt.android.AndroidEntryPoint +import designsystem.components.bottomsheet.BottomSheetType import designsystem.components.bottomsheet.LinkMindBottomSheet import designsystem.components.dialog.LinkMindDialog import designsystem.components.toast.linkMindSnackBar @@ -105,6 +106,7 @@ class ClipEditFragment : BindingFragment({ FragmentClip val editTitleBottomSheet = LinkMindBottomSheet(requireContext()) editTitleBottomSheet.show() editTitleBottomSheet.apply { + setBottomSheetType(BottomSheetType.CLIP) setBottomSheetHint(org.sopt.mainfeature.R.string.home_new_clip_info) setTitle(org.sopt.mainfeature.R.string.edit_clip_edit_title) setBottomSheetText(itemText) diff --git a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt index 771c720a..d420d03d 100644 --- a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.flowWithLifecycle import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint +import designsystem.components.bottomsheet.BottomSheetType import designsystem.components.bottomsheet.LinkMindBottomSheet import designsystem.components.toast.linkMindSnackBar import kotlinx.coroutines.flow.launchIn @@ -264,10 +265,10 @@ class ClipLinkFragment : BindingFragment({ FragmentClip val editTitleBottomSheet = LinkMindBottomSheet(requireContext()) editTitleBottomSheet.show() editTitleBottomSheet.apply { + setBottomSheetType(BottomSheetType.LINK) setBottomSheetHint(itemText) setTitle(org.sopt.mainfeature.R.string.clip_link_bottom_sheet_modify_title) setBottomSheetText(itemText) - setErroMsg(org.sopt.mainfeature.R.string.error_clip_length) bottomSheetConfirmBtnClick { // dto 수정됨 val newTitle = getText() viewModel.patchLinkTitle(itemId, newTitle) diff --git a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt index 70926045..58487d1d 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt @@ -5,6 +5,7 @@ import android.view.View import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint +import designsystem.components.bottomsheet.BottomSheetType import designsystem.components.bottomsheet.LinkMindBottomSheet import designsystem.components.toast.linkMindSnackBar import org.orbitmvi.orbit.viewmodel.observe @@ -143,11 +144,12 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. val linkMindBottomSheet = LinkMindBottomSheet(requireContext()) linkMindBottomSheet.show() linkMindBottomSheet.apply { + setBottomSheetType(BottomSheetType.CLIP) setBottomSheetHint(org.sopt.mainfeature.R.string.home_new_clip_info) setTitle(org.sopt.mainfeature.R.string.home_add_clip) setErroMsg(org.sopt.mainfeature.R.string.home_error_clip_info) bottomSheetConfirmBtnClick { - if (showErrorMsg()) return@bottomSheetConfirmBtnClick + if (showErrorMsg(BottomSheetType.CLIP)) return@bottomSheetConfirmBtnClick viewModel.saveCategoryTitle(it) dismiss() requireContext().linkMindSnackBar(binding.vSnack, "클립 생성 완료!", false) diff --git a/feature/savelink/src/main/java/org/sopt/savelink/ui/savelinksetclip/SaveLinkSetClipFragment.kt b/feature/savelink/src/main/java/org/sopt/savelink/ui/savelinksetclip/SaveLinkSetClipFragment.kt index 867fce6d..3266aad6 100644 --- a/feature/savelink/src/main/java/org/sopt/savelink/ui/savelinksetclip/SaveLinkSetClipFragment.kt +++ b/feature/savelink/src/main/java/org/sopt/savelink/ui/savelinksetclip/SaveLinkSetClipFragment.kt @@ -6,6 +6,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.viewModelScope import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint +import designsystem.components.bottomsheet.BottomSheetType import designsystem.components.bottomsheet.LinkMindBottomSheet import designsystem.components.button.state.LinkMindButtonState import designsystem.components.dialog.LinkMindDialog @@ -137,12 +138,13 @@ class SaveLinkSetClipFragment : BindingFragment( val linkMindBottomSheet = LinkMindBottomSheet(requireContext()) linkMindBottomSheet.show() linkMindBottomSheet.apply { + setBottomSheetType(BottomSheetType.CLIP) setTitle(R.string.clip_add_clip) setErroMsg(R.string.error_clip_length) setBottomSheetHint(R.string.home_new_clip_info) bottomSheetConfirmBtnClick { viewModel.getCategoryDuplicate(it) - if (showErrorMsg()) return@bottomSheetConfirmBtnClick + if (showErrorMsg(BottomSheetType.CLIP)) return@bottomSheetConfirmBtnClick dismiss() } } From 023bab68741d12f6f2dd6d36474ea25e141386e2 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Wed, 6 Nov 2024 03:37:48 +0900 Subject: [PATCH 2/5] [feat] #182-clip-change-ui --- .../main/java/org/sopt/model/timer}/Clip.kt | 2 +- .../java/org/sopt/clip/ClipChangeAdapter.kt | 62 ++++++++ .../java/org/sopt/clip/ClipChangeFragment.kt | 107 +++++++++++++ .../org/sopt/clip/ClipChangeViewHolder.kt | 48 ++++++ .../clip/DeleteLinkBottomSheetFragment.kt | 48 ++++-- .../sopt/clip/cliplink/ClipLinkFragment.kt | 55 +++++-- .../sopt/clip/cliplink/ClipLinkViewModel.kt | 22 +++ .../main/res/layout/fragment_clip_change.xml | 61 +++++++ .../fragment_delete_link_bottom_sheet.xml | 150 ++++++++++-------- .../src/main/res/layout/item_clip_change.xml | 42 +++++ .../main/res/navigation/nav_graph_clip.xml | 37 +++-- .../sopt/timer/settimer/SetTimerViewModel.kt | 4 +- .../settimer/clipselect/ClipSelectAdapter.kt | 2 +- .../clipselect/ClipSelectViewHolder.kt | 2 +- .../clipselect/TimerClipSelectFragment.kt | 4 +- 15 files changed, 540 insertions(+), 106 deletions(-) rename {feature/timer/src/main/java/org/sopt/timer/model => core/model/src/main/java/org/sopt/model/timer}/Clip.kt (91%) create mode 100644 feature/clip/src/main/java/org/sopt/clip/ClipChangeAdapter.kt create mode 100644 feature/clip/src/main/java/org/sopt/clip/ClipChangeFragment.kt create mode 100644 feature/clip/src/main/java/org/sopt/clip/ClipChangeViewHolder.kt create mode 100644 feature/clip/src/main/res/layout/fragment_clip_change.xml create mode 100644 feature/clip/src/main/res/layout/item_clip_change.xml diff --git a/feature/timer/src/main/java/org/sopt/timer/model/Clip.kt b/core/model/src/main/java/org/sopt/model/timer/Clip.kt similarity index 91% rename from feature/timer/src/main/java/org/sopt/timer/model/Clip.kt rename to core/model/src/main/java/org/sopt/model/timer/Clip.kt index 8aa98ef0..01475ecb 100644 --- a/feature/timer/src/main/java/org/sopt/timer/model/Clip.kt +++ b/core/model/src/main/java/org/sopt/model/timer/Clip.kt @@ -1,4 +1,4 @@ -package org.sopt.timer.model +package org.sopt.model.timer import org.sopt.model.category.Category diff --git a/feature/clip/src/main/java/org/sopt/clip/ClipChangeAdapter.kt b/feature/clip/src/main/java/org/sopt/clip/ClipChangeAdapter.kt new file mode 100644 index 00000000..e835ad32 --- /dev/null +++ b/feature/clip/src/main/java/org/sopt/clip/ClipChangeAdapter.kt @@ -0,0 +1,62 @@ +package org.sopt.clip + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.ListAdapter +import org.sopt.clip.databinding.ItemClipChangeBinding +import org.sopt.model.timer.Clip +import org.sopt.ui.view.ItemDiffCallback + +class ClipChangeAdapter( + private val onClick: (Clip, Int) -> Unit, + private val context: Context, +) : ListAdapter(DiffUtil) { + var selectedPosition = -1 + override fun onBindViewHolder(holder: ClipChangeViewHolder, position: Int) { + holder.onBind(getItem(position), position) { clip, position -> + selectItemByPosition(position, clip) + onClick(clip, position) + } + + if (position == 0) { + val disMissClickColor = ContextCompat.getColor(context, org.sopt.mainfeature.R.color.neutrals400) + holder.binding.ivItemClipChange.setColorFilter(disMissClickColor) + holder.binding.tvItemClipChangeName.setTextColor(disMissClickColor) + holder.binding.tvItemClipChangeCount.setTextColor(disMissClickColor) + holder.binding.root.isEnabled = false + } + } + + private fun selectItemByPosition(position: Int, clip: Clip) { + if (selectedPosition != position) { + if (selectedPosition != -1) { + getItem(selectedPosition).isSelected = false + notifyItemChanged(selectedPosition) + } + clip.isSelected = true + selectedPosition = position + } else { + clip.isSelected = !clip.isSelected + if (!clip.isSelected) { + selectedPosition = -1 + } + } + notifyItemChanged(position) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ClipChangeViewHolder { + return ClipChangeViewHolder( + ItemClipChangeBinding.inflate(LayoutInflater.from(parent.context), parent, false), + context, + ) + } + + companion object { + private val DiffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old == new }, + onContentsTheSame = { old, new -> old == new }, + ) + } +} diff --git a/feature/clip/src/main/java/org/sopt/clip/ClipChangeFragment.kt b/feature/clip/src/main/java/org/sopt/clip/ClipChangeFragment.kt new file mode 100644 index 00000000..6831ea33 --- /dev/null +++ b/feature/clip/src/main/java/org/sopt/clip/ClipChangeFragment.kt @@ -0,0 +1,107 @@ +package org.sopt.clip + +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import dagger.hilt.android.AndroidEntryPoint +import designsystem.components.button.state.LinkMindButtonState +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.sopt.clip.cliplink.ClipLinkViewModel +import org.sopt.clip.databinding.FragmentClipChangeBinding +import org.sopt.model.timer.Clip +import org.sopt.model.timer.toUiModel +import org.sopt.ui.base.BindingFragment +import org.sopt.ui.fragment.viewLifeCycle +import org.sopt.ui.fragment.viewLifeCycleScope +import org.sopt.ui.view.UiState +import org.sopt.ui.view.onThrottleClick + +@AndroidEntryPoint +class ClipChangeFragment : + BindingFragment({ FragmentClipChangeBinding.inflate(it) }) { + private lateinit var clipAdapter: ClipChangeAdapter + private val viewModel: ClipLinkViewModel by activityViewModels() + //TODO. 현재 id = 리스트에서 빼던지, id받아와서 리사이클러뷰 아이템에서 뺴기 + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val args: ClipChangeFragmentArgs by navArgs() + + getCategoryAll() + collectClipState(args) + initCloseButtonClickListener() + } + + private fun getCategoryAll() { + viewModel.getCategoryAll() + + } + + private fun collectClipState(args: ClipChangeFragmentArgs) { + viewModel.categoryState.flowWithLifecycle(viewLifeCycle).onEach { state -> + when (state) { + is UiState.Success -> { + initClipSelectAdapter(state.data.toUiModel(), args.toastId, args.clipId) + } + + else -> {} + } + }.launchIn(viewLifeCycleScope) + } + + private fun initClipSelectAdapter(list: List, toastId: Long, currentClipId: Long) { + val clipList = excludeCurrentClipId(list, currentClipId) + clipAdapter = ClipChangeAdapter( + onClick = { clip, index -> + handleClipClick(clip, clipList, index, clip.id, toastId) + }, + context = requireContext(), + ) + clipAdapter.selectedPosition = clipList.indexOfFirst { it.isSelected } + clipAdapter.submitList(clipList) + binding.btnClipChangeSelectNext.state = if (clipAdapter.selectedPosition != -1) LinkMindButtonState.ENABLE else LinkMindButtonState.DISABLE + binding.rvClipChangeSelect.adapter = clipAdapter + binding.rvClipChangeSelect.itemAnimator = null + initNextButtonClickListener() + } + + private fun handleClipClick( + clip: Clip, + list: List, + index: Int, + newClipId: Long, + toastId: Long, + ) { + if (clip.isSelected) { + list.onEach { it.isSelected = false } + list[index].isSelected = true + binding.btnClipChangeSelectNext.state = LinkMindButtonState.ENABLE + } else { + list.onEach { it.isSelected = false } + binding.btnClipChangeSelectNext.state = LinkMindButtonState.DISABLE + } + } + + private fun initNextButtonClickListener() { + binding.btnClipChangeSelectNext.btnClick { + Log.d("asdasd", "next") + } + } + + private fun initCloseButtonClickListener() { + binding.ivClipChangeClose.onThrottleClick { + Log.d("asdasd", "close") + findNavController().popBackStack() + } + } + + private fun excludeCurrentClipId(clipList: List, currentClipId: Long): List = + clipList.filter { it.id != currentClipId } +} diff --git a/feature/clip/src/main/java/org/sopt/clip/ClipChangeViewHolder.kt b/feature/clip/src/main/java/org/sopt/clip/ClipChangeViewHolder.kt new file mode 100644 index 00000000..1a3b422a --- /dev/null +++ b/feature/clip/src/main/java/org/sopt/clip/ClipChangeViewHolder.kt @@ -0,0 +1,48 @@ +package org.sopt.clip + +import android.content.Context +import androidx.recyclerview.widget.RecyclerView +import org.sopt.clip.databinding.ItemClipChangeBinding +import org.sopt.model.timer.Clip +import org.sopt.ui.view.onThrottleClick + +class ClipChangeViewHolder( + val binding: ItemClipChangeBinding, + val context: Context, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind(data: Clip?, pos: Int, onClick: (Clip, Int) -> Unit) { + if (data == null) return + with(binding) { + tvItemClipChangeName.text = data.name + tvItemClipChangeCount.text = "${data.count}개" + setSelectedClipColor(data, pos) + root.onThrottleClick { + onClick(data, bindingAdapterPosition) + bindingAdapter?.notifyItemChanged(pos) + } + } + } + + private fun ItemClipChangeBinding.setSelectedClipColor( + data: Clip, + pos: Int, + ) { + val selectedColor = androidx.core.content.ContextCompat.getColor(context, org.sopt.mainfeature.R.color.primary) + val defaultColor = androidx.core.content.ContextCompat.getColor(context, org.sopt.mainfeature.R.color.neutrals900) + if (data.isSelected) { + ivItemClipChange.setImageResource( + org.sopt.mainfeature.R.drawable.ic_clip_all_24_primary.takeIf { pos == 0 } + ?: org.sopt.mainfeature.R.drawable.ic_clip_24_primary, + ) + tvItemClipChangeCount.setTextColor(selectedColor) + tvItemClipChangeName.setTextColor(selectedColor) + } else { + ivItemClipChange.setImageResource( + org.sopt.mainfeature.R.drawable.ic_clip_all_24.takeIf { pos == 0 } + ?: org.sopt.mainfeature.R.drawable.ic_clip_24, + ) + tvItemClipChangeCount.setTextColor(defaultColor) + tvItemClipChangeName.setTextColor(defaultColor) + } + } +} diff --git a/feature/clip/src/main/java/org/sopt/clip/DeleteLinkBottomSheetFragment.kt b/feature/clip/src/main/java/org/sopt/clip/DeleteLinkBottomSheetFragment.kt index 10fa582c..c32f0262 100644 --- a/feature/clip/src/main/java/org/sopt/clip/DeleteLinkBottomSheetFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/DeleteLinkBottomSheetFragment.kt @@ -1,48 +1,76 @@ package org.sopt.clip import android.os.Bundle -import android.util.Log import android.view.View +import androidx.core.view.isVisible import org.sopt.clip.databinding.FragmentDeleteLinkBottomSheetBinding import org.sopt.ui.base.BindingBottomSheetDialogFragment import org.sopt.ui.view.onThrottleClick class DeleteLinkBottomSheetFragment() : BindingBottomSheetDialogFragment({ FragmentDeleteLinkBottomSheetBinding.inflate(it) }) { - var id: Int? = null + var clipId: Long? = null + var isFullClipSize: Boolean? = null private var handleDelete: () -> Unit = {} + private var handleChange: () -> Unit = {} private var handleModify: () -> Unit = {} + private var isClipListEmptySnackBar: () -> Unit = {} + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + clipId = arguments?.getLong("clipId") + isFullClipSize = arguments?.getBoolean("isFullClipSize") + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + if (clipId?.toInt() == 0) { + binding.tvDeleteLinkChange.isVisible = false + } + binding.ivDeleteLinkBottomSheetClose.setOnClickListener { dismiss() } binding.tvDeleteLinkDelete.onThrottleClick { - Log.d("test", "test") handleDelete.invoke() dismiss() } + + binding.tvDeleteLinkChange.onThrottleClick { + if (isFullClipSize == true) { + handleChange.invoke() + } else { + isClipListEmptySnackBar.invoke() + } + dismiss() + } + binding.tvDeleteLinkModify.onThrottleClick { handleModify.invoke() dismiss() } } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - id = arguments?.getInt("id") - } - companion object { - fun newInstance(id: Int, handleDeleteButton: () -> Unit, handleModifyButton: () -> Unit): DeleteLinkBottomSheetFragment { + fun newInstance( + clipId: Long, + isFullClipSize: Boolean, + isClipListEmpty: () -> Unit, + handleDeleteButton: () -> Unit, + handleChangeButton: () -> Unit, + handleModifyButton: () -> Unit, + ): DeleteLinkBottomSheetFragment { val args = Bundle().apply { - putInt("id", id) + putLong("clipId", clipId) + putBoolean("isFullClipSize", isFullClipSize) } return DeleteLinkBottomSheetFragment().apply { arguments = args handleDelete = handleDeleteButton + handleChange = handleChangeButton handleModify = handleModifyButton + isClipListEmptySnackBar = isClipListEmpty } } } diff --git a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt index d420d03d..927a9305 100644 --- a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt @@ -1,6 +1,7 @@ package org.sopt.clip.cliplink import android.os.Bundle +import android.util.Log import android.view.View import android.widget.TextView import androidx.core.view.isVisible @@ -16,8 +17,10 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.sopt.clip.DeleteLinkBottomSheetFragment import org.sopt.clip.R +import org.sopt.clip.clip.ClipFragmentDirections import org.sopt.clip.databinding.FragmentClipLinkBinding import org.sopt.common.util.delSpace +import org.sopt.model.category.Category import org.sopt.ui.base.BindingFragment import org.sopt.ui.fragment.colorOf import org.sopt.ui.fragment.viewLifeCycle @@ -32,10 +35,12 @@ import java.nio.charset.StandardCharsets class ClipLinkFragment : BindingFragment({ FragmentClipLinkBinding.inflate(it) }) { private val viewModel: ClipLinkViewModel by viewModels() private lateinit var clipLinkAdapter: ClipLinkAdapter - var isDataNull: Boolean = true + private var isDataNull: Boolean = true override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.initState() + viewModel.getCategoryAll() + val args: ClipLinkFragmentArgs by navArgs() val categoryId = args.categoryId val categoryName = args.categoryName @@ -81,7 +86,7 @@ class ClipLinkFragment : BindingFragment({ FragmentClip binding.tvClipLinkRead, ) } - initClipAdapter() + initClipAdapter(args.categoryId) initViewState(isDataNull) updateLinkDelete(categoryId) updateLinkView() @@ -128,6 +133,7 @@ class ClipLinkFragment : BindingFragment({ FragmentClip } }.launchIn(viewLifeCycleScope) } + private fun updateAllCount() { viewModel.allClipCount.flowWithLifecycle(viewLifeCycle).onEach { state -> when (state) { @@ -238,7 +244,7 @@ class ClipLinkFragment : BindingFragment({ FragmentClip } } - private fun initClipAdapter() { + private fun initClipAdapter(clipId: Long) { clipLinkAdapter = ClipLinkAdapter { linkDTO, state -> when (state) { "click" -> { @@ -246,15 +252,23 @@ class ClipLinkFragment : BindingFragment({ FragmentClip } "delete" -> { - DeleteLinkBottomSheetFragment.newInstance( - linkDTO.toastId.toInt(), - handleDeleteButton = { - viewModel.deleteLink(linkDTO.toastId) - }, - handleModifyButton = { - showClipLinkBottomSheet(linkDTO.toastId, linkDTO.toastTitle) - }, - ).show(parentFragmentManager, this.tag) + getAllClip { categoryList -> + val isFullClipSize = categoryList.size > 2 + DeleteLinkBottomSheetFragment.newInstance( + clipId, + isFullClipSize, + {requireContext().linkMindSnackBar(binding.vSnack, "클립 하나임", true)}, + handleDeleteButton = { + viewModel.deleteLink(linkDTO.toastId) + }, + handleChangeButton = { + navigateToDestination("featureClipChange://clipChangeFragment/$clipId/${linkDTO.toastId}") + }, + handleModifyButton = { + showClipLinkBottomSheet(linkDTO.toastId, linkDTO.toastTitle) + }, + ).show(parentFragmentManager, this.tag) + } } } } @@ -277,6 +291,23 @@ class ClipLinkFragment : BindingFragment({ FragmentClip } } + private fun getAllClip(callback: (List) -> Unit) { + viewModel.categoryState + .flowWithLifecycle(viewLifeCycle) + .onEach { state -> + when (state) { + is UiState.Success -> { + callback(state.data) + } + + else -> { + initViewState(true) + } + } + } + .launchIn(viewLifeCycleScope) + } + private fun naviagateToWebViewFragment(site: String, toastId: Long, isRead: Boolean) { val encodedURL = URLEncoder.encode(site, StandardCharsets.UTF_8.toString()) navigateToDestination("featureSaveLink://webViewFragment/$toastId/$isRead/${true}/$encodedURL") diff --git a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkViewModel.kt b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkViewModel.kt index 1f4b01dc..db35b171 100644 --- a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkViewModel.kt +++ b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkViewModel.kt @@ -9,9 +9,11 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import org.sopt.clip.SelectedToggle +import org.sopt.domain.category.category.usecase.GetCategoryAllUseCase import org.sopt.domain.category.category.usecase.GetCategoryLinkUseCase import org.sopt.domain.link.usecase.DeleteLinkUseCase import org.sopt.domain.link.usecase.PatchLinkTitleUseCase +import org.sopt.model.category.Category import org.sopt.model.category.CategoryLink import org.sopt.ui.view.UiState import javax.inject.Inject @@ -21,6 +23,7 @@ class ClipLinkViewModel @Inject constructor( private val getCategoryLink: GetCategoryLinkUseCase, private val deleteLinkUseCase: DeleteLinkUseCase, private val patchLinkTitleUseCase: PatchLinkTitleUseCase, + private val getCategoryAll: GetCategoryAllUseCase, ) : ViewModel() { private val _linkState = MutableStateFlow>>(UiState.Empty) val linkState: StateFlow>> = _linkState.asStateFlow() @@ -34,6 +37,9 @@ class ClipLinkViewModel @Inject constructor( private val _patchLinkTitle = MutableStateFlow>(UiState.Empty) val patchLinkTitle: StateFlow> = _patchLinkTitle.asStateFlow() + private val _categoryState = MutableStateFlow>>(UiState.Empty) + val categoryState: StateFlow>> = _categoryState.asStateFlow() + var toggleSelectedPast: SelectedToggle = SelectedToggle.ALL fun deleteLink(toastId: Long) = viewModelScope.launch { deleteLinkUseCase.invoke(param = DeleteLinkUseCase.Param(toastId = toastId)).onSuccess { @@ -50,6 +56,7 @@ class ClipLinkViewModel @Inject constructor( fun updateDeleteState() = viewModelScope.launch { _deleteState.emit(UiState.Success(false)) } + fun getCategoryLink(filter: String?, categoryId: Long?) = viewModelScope.launch { getCategoryLink(param = GetCategoryLinkUseCase.Param(filter = filter, categoryId = categoryId)).onSuccess { val list: MutableList = it.toastListDto.toMutableList() @@ -68,6 +75,21 @@ class ClipLinkViewModel @Inject constructor( } } + fun getCategoryAll() = viewModelScope.launch { + getCategoryAll.invoke().onSuccess { + val allCategoryList = listOf( + Category(0, "전체 클립", it.toastNumberInEntire), + ) + _categoryState.emit(UiState.Success(allCategoryList + it.categories)) + }.onFailure { + Log.e("실패", it.message.toString()) + } + } + + fun patchClipChange() { + + } + fun initState() { _linkState.value = UiState.Empty } diff --git a/feature/clip/src/main/res/layout/fragment_clip_change.xml b/feature/clip/src/main/res/layout/fragment_clip_change.xml new file mode 100644 index 00000000..8b4fe0fe --- /dev/null +++ b/feature/clip/src/main/res/layout/fragment_clip_change.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + diff --git a/feature/clip/src/main/res/layout/fragment_delete_link_bottom_sheet.xml b/feature/clip/src/main/res/layout/fragment_delete_link_bottom_sheet.xml index 8d8760d0..f43f25be 100644 --- a/feature/clip/src/main/res/layout/fragment_delete_link_bottom_sheet.xml +++ b/feature/clip/src/main/res/layout/fragment_delete_link_bottom_sheet.xml @@ -1,75 +1,95 @@ - - - - - - + android:background="@drawable/shape_neutrals_fill_top20_rect"> + android:id="@+id/tv_delete_link_bottom_sheet_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="20dp" + android:layout_marginTop="21dp" + android:text="@string/clip_link_bottom_sheet_title" + android:textAppearance="@style/Typography.suit.bold_18" + android:textColor="@color/neutrals_black" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - + - + + + + + + + + + + diff --git a/feature/clip/src/main/res/layout/item_clip_change.xml b/feature/clip/src/main/res/layout/item_clip_change.xml new file mode 100644 index 00000000..4cdbdee7 --- /dev/null +++ b/feature/clip/src/main/res/layout/item_clip_change.xml @@ -0,0 +1,42 @@ + + + + + + + diff --git a/feature/clip/src/main/res/navigation/nav_graph_clip.xml b/feature/clip/src/main/res/navigation/nav_graph_clip.xml index f8bf148d..7e967fa0 100644 --- a/feature/clip/src/main/res/navigation/nav_graph_clip.xml +++ b/feature/clip/src/main/res/navigation/nav_graph_clip.xml @@ -8,11 +8,10 @@ android:id="@+id/navigation_clip" android:name="org.sopt.clip.clip.ClipFragment" android:label="fragment_clip" - tools:layout="@layout/fragment_clip" > + tools:layout="@layout/fragment_clip"> - + app:destination="@id/navigation_clip_link"> @@ -24,20 +23,22 @@ tools:layout="@layout/fragment_clip_link"> + app:argType="long" /> - + app:argType="string" /> + + + android:label="WebViewFragment"> @@ -50,19 +51,31 @@ - + + tools:layout="@layout/fragment_clip_edit" /> + tools:layout="@layout/fragment_search"> + + + + + diff --git a/feature/timer/src/main/java/org/sopt/timer/settimer/SetTimerViewModel.kt b/feature/timer/src/main/java/org/sopt/timer/settimer/SetTimerViewModel.kt index b9a84895..58f423e7 100644 --- a/feature/timer/src/main/java/org/sopt/timer/settimer/SetTimerViewModel.kt +++ b/feature/timer/src/main/java/org/sopt/timer/settimer/SetTimerViewModel.kt @@ -11,9 +11,9 @@ import kotlinx.coroutines.launch import org.sopt.domain.category.category.usecase.GetCategoryAllUseCase import org.sopt.model.category.Category import org.sopt.model.timer.Repeat -import org.sopt.timer.model.Clip +import org.sopt.model.timer.Clip import org.sopt.timer.model.TimePicker -import org.sopt.timer.model.toUiModel +import org.sopt.model.timer.toUiModel import org.sopt.timer.usecase.FormatRepeatListToIntList import org.sopt.timer.usecase.FormatRepeatListToStringList import org.sopt.timer.usecase.PatchTimerUseCase diff --git a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt index 3af88a0c..5f7ff6bf 100644 --- a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt +++ b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt @@ -5,7 +5,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter import org.sopt.timer.databinding.ItemTimerClipSelectBinding -import org.sopt.timer.model.Clip +import org.sopt.model.timer.Clip import org.sopt.ui.view.ItemDiffCallback class ClipSelectAdapter( diff --git a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt index 8dcfacce..a405eae1 100644 --- a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt +++ b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt @@ -4,7 +4,7 @@ import android.content.Context import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import org.sopt.timer.databinding.ItemTimerClipSelectBinding -import org.sopt.timer.model.Clip +import org.sopt.model.timer.Clip import org.sopt.ui.view.onThrottleClick class ClipSelectViewHolder( diff --git a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt index a3d6ad0e..c192490e 100644 --- a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt +++ b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.sopt.timer.R import org.sopt.timer.databinding.FragmentTimerClipSelectBinding -import org.sopt.timer.model.Clip +import org.sopt.model.timer.Clip import org.sopt.timer.settimer.SetTimerViewModel import org.sopt.ui.base.BindingFragment import org.sopt.ui.fragment.viewLifeCycle @@ -28,8 +28,8 @@ class TimerClipSelectFragment : BindingFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) getCategoryAll() - initCloseButtonClickListener() collectClipState() + initCloseButtonClickListener() } private fun getCategoryAll() { From ca6c88b2baba62520b8b5e52ee9138de96d1f750 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Wed, 6 Nov 2024 04:43:09 +0900 Subject: [PATCH 3/5] [feat] #182 clip change api --- .../org/sopt/remote/link/api/LinkService.kt | 6 +++++ .../datasource/RemoteLinkDataSourceImpl.kt | 9 ++++++++ .../link/request/RequestPatchCategoryDto.kt | 12 ++++++++++ .../link/response/ResponsePatchCategoryDto.kt | 10 ++++++++ .../link/datasource/RemoteLinkDataSource.kt | 2 +- .../sopt/data/link/repository/LinkRepoImpl.kt | 3 +++ .../domain/link/repository/LinkRepository.kt | 2 +- .../link/usecase/PatchLinkCategoryUseCase.kt | 18 +++++++++++++++ .../{ => clipchange}/ClipChangeAdapter.kt | 2 +- .../{ => clipchange}/ClipChangeFragment.kt | 20 +++++++--------- .../{ => clipchange}/ClipChangeViewHolder.kt | 2 +- .../sopt/clip/clipedit/ClipEditFragment.kt | 2 +- .../sopt/clip/cliplink/ClipLinkFragment.kt | 23 ++++++++++++++----- .../sopt/clip/cliplink/ClipLinkViewModel.kt | 14 +++++++++-- .../main/res/layout/fragment_clip_change.xml | 6 +++++ .../main/res/navigation/nav_graph_clip.xml | 2 +- 16 files changed, 107 insertions(+), 26 deletions(-) create mode 100644 data-remote/link/src/main/java/org/sopt/remote/link/request/RequestPatchCategoryDto.kt create mode 100644 data-remote/link/src/main/java/org/sopt/remote/link/response/ResponsePatchCategoryDto.kt create mode 100644 domain/link/src/main/java/org/sopt/domain/link/usecase/PatchLinkCategoryUseCase.kt rename feature/clip/src/main/java/org/sopt/clip/{ => clipchange}/ClipChangeAdapter.kt (98%) rename feature/clip/src/main/java/org/sopt/clip/{ => clipchange}/ClipChangeFragment.kt (89%) rename feature/clip/src/main/java/org/sopt/clip/{ => clipchange}/ClipChangeViewHolder.kt (98%) diff --git a/data-remote/link/src/main/java/org/sopt/remote/link/api/LinkService.kt b/data-remote/link/src/main/java/org/sopt/remote/link/api/LinkService.kt index d9cc306b..035ec160 100644 --- a/data-remote/link/src/main/java/org/sopt/remote/link/api/LinkService.kt +++ b/data-remote/link/src/main/java/org/sopt/remote/link/api/LinkService.kt @@ -2,9 +2,11 @@ package org.sopt.remote.link.api import org.sopt.network.model.response.base.BaseResponse import org.sopt.remote.link.request.RequestIsReadDto +import org.sopt.remote.link.request.RequestPatchCategoryDto import org.sopt.remote.link.request.RequestPatchTitleDto import org.sopt.remote.link.request.RequestWriteDto import org.sopt.remote.link.response.ResponseIsReadDto +import org.sopt.remote.link.response.ResponsePatchCategoryDto import org.sopt.remote.link.response.ResponsePatchTitleDto import retrofit2.http.Body import retrofit2.http.DELETE @@ -19,6 +21,7 @@ interface LinkService { const val ISREAD = "is-read" const val SAVE = "save" const val TITLE = "title" + const val CATEGORY = "category" } @POST("/$TOAST/$SAVE") @@ -34,4 +37,7 @@ interface LinkService { @PATCH("/$TOAST/$TITLE") suspend fun patchLinkTitle(@Body requestPatchTitleDto: RequestPatchTitleDto): BaseResponse + + @PATCH("/$TOAST/$CATEGORY") + suspend fun patchToastCategory(@Body requestPatchCategoryDto: RequestPatchCategoryDto): BaseResponse } diff --git a/data-remote/link/src/main/java/org/sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt b/data-remote/link/src/main/java/org/sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt index ca8404b0..2ce9094b 100644 --- a/data-remote/link/src/main/java/org/sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt +++ b/data-remote/link/src/main/java/org/sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt @@ -3,6 +3,7 @@ package org.sopt.remote.link.datasource import org.sopt.data.link.datasource.RemoteLinkDataSource import org.sopt.remote.link.api.LinkService import org.sopt.remote.link.request.RequestIsReadDto +import org.sopt.remote.link.request.RequestPatchCategoryDto import org.sopt.remote.link.request.RequestPatchTitleDto import org.sopt.remote.link.request.RequestWriteDto import javax.inject.Inject @@ -37,4 +38,12 @@ class RemoteLinkDataSourceImpl @Inject constructor( title = title, ), ).data!!.updatedTitle + + override suspend fun patchLinkCategory(toastId: Long, categoryId: Long): Long = + linkService.patchToastCategory( + RequestPatchCategoryDto( + toastId = toastId, + categoryId = categoryId + ), + ).data!!.categoryId } diff --git a/data-remote/link/src/main/java/org/sopt/remote/link/request/RequestPatchCategoryDto.kt b/data-remote/link/src/main/java/org/sopt/remote/link/request/RequestPatchCategoryDto.kt new file mode 100644 index 00000000..f699d3d9 --- /dev/null +++ b/data-remote/link/src/main/java/org/sopt/remote/link/request/RequestPatchCategoryDto.kt @@ -0,0 +1,12 @@ +package org.sopt.remote.link.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestPatchCategoryDto( + @SerialName("toastId") + val toastId: Long, + @SerialName("categoryId") + val categoryId: Long, +) diff --git a/data-remote/link/src/main/java/org/sopt/remote/link/response/ResponsePatchCategoryDto.kt b/data-remote/link/src/main/java/org/sopt/remote/link/response/ResponsePatchCategoryDto.kt new file mode 100644 index 00000000..63716e2e --- /dev/null +++ b/data-remote/link/src/main/java/org/sopt/remote/link/response/ResponsePatchCategoryDto.kt @@ -0,0 +1,10 @@ +package org.sopt.remote.link.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponsePatchCategoryDto( + @SerialName("categoryId") + val categoryId: Long, +) diff --git a/data/link/src/main/java/org/sopt/data/link/datasource/RemoteLinkDataSource.kt b/data/link/src/main/java/org/sopt/data/link/datasource/RemoteLinkDataSource.kt index 214d4592..80278a7d 100644 --- a/data/link/src/main/java/org/sopt/data/link/datasource/RemoteLinkDataSource.kt +++ b/data/link/src/main/java/org/sopt/data/link/datasource/RemoteLinkDataSource.kt @@ -4,6 +4,6 @@ interface RemoteLinkDataSource { suspend fun postSaveLink(linkUrl: String, categoryId: Long?): Int suspend fun deleteLink(toastId: Long): Int suspend fun patchReadLink(toastId: Long, isRead: Boolean): Boolean - suspend fun patchLinkTitle(toastId: Long, title: String): String + suspend fun patchLinkCategory(toastId: Long, categoryId: Long): Long } diff --git a/data/link/src/main/java/org/sopt/data/link/repository/LinkRepoImpl.kt b/data/link/src/main/java/org/sopt/data/link/repository/LinkRepoImpl.kt index 7fbc0fcf..ee617a55 100644 --- a/data/link/src/main/java/org/sopt/data/link/repository/LinkRepoImpl.kt +++ b/data/link/src/main/java/org/sopt/data/link/repository/LinkRepoImpl.kt @@ -18,4 +18,7 @@ class LinkRepoImpl @Inject constructor( override suspend fun patchLinkTitle(toastId: Long, title: String): Result = runCatching { remoteCategoryDataSource.patchLinkTitle(toastId, title) } + + override suspend fun patchToastCategory(toastId: Long, categoryId: Long): Result = + runCatching { remoteCategoryDataSource.patchLinkCategory(toastId, categoryId) } } diff --git a/domain/link/src/main/java/org/sopt/domain/link/repository/LinkRepository.kt b/domain/link/src/main/java/org/sopt/domain/link/repository/LinkRepository.kt index 08c3798b..2e4a02f6 100644 --- a/domain/link/src/main/java/org/sopt/domain/link/repository/LinkRepository.kt +++ b/domain/link/src/main/java/org/sopt/domain/link/repository/LinkRepository.kt @@ -4,6 +4,6 @@ interface LinkRepository { suspend fun postSaveLink(linkUrl: String, categoryId: Long?): Result suspend fun deleteLink(toastId: Long): Result suspend fun patchReadLink(toastId: Long, isRead: Boolean): Result - suspend fun patchLinkTitle(toastId: Long, title: String): Result + suspend fun patchToastCategory(toastId: Long, categoryId: Long): Result } diff --git a/domain/link/src/main/java/org/sopt/domain/link/usecase/PatchLinkCategoryUseCase.kt b/domain/link/src/main/java/org/sopt/domain/link/usecase/PatchLinkCategoryUseCase.kt new file mode 100644 index 00000000..c527b587 --- /dev/null +++ b/domain/link/src/main/java/org/sopt/domain/link/usecase/PatchLinkCategoryUseCase.kt @@ -0,0 +1,18 @@ +package org.sopt.domain.link.usecase + +import org.sopt.domain.link.repository.LinkRepository +import javax.inject.Inject + +class PatchLinkCategoryUseCase @Inject constructor( + private val linkRepository: LinkRepository, +) { + suspend operator fun invoke(param: Param): Result = linkRepository.patchToastCategory( + toastId = param.toastId, + categoryId = param.categoryId, + ) + + data class Param( + val toastId: Long, + val categoryId: Long, + ) +} diff --git a/feature/clip/src/main/java/org/sopt/clip/ClipChangeAdapter.kt b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeAdapter.kt similarity index 98% rename from feature/clip/src/main/java/org/sopt/clip/ClipChangeAdapter.kt rename to feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeAdapter.kt index e835ad32..c82fcaad 100644 --- a/feature/clip/src/main/java/org/sopt/clip/ClipChangeAdapter.kt +++ b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeAdapter.kt @@ -1,4 +1,4 @@ -package org.sopt.clip +package org.sopt.clip.clipchange import android.content.Context import android.view.LayoutInflater diff --git a/feature/clip/src/main/java/org/sopt/clip/ClipChangeFragment.kt b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeFragment.kt similarity index 89% rename from feature/clip/src/main/java/org/sopt/clip/ClipChangeFragment.kt rename to feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeFragment.kt index 6831ea33..ede46c5d 100644 --- a/feature/clip/src/main/java/org/sopt/clip/ClipChangeFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeFragment.kt @@ -1,7 +1,6 @@ -package org.sopt.clip +package org.sopt.clip.clipchange import android.os.Bundle -import android.util.Log import android.view.View import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle @@ -9,6 +8,7 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint import designsystem.components.button.state.LinkMindButtonState +import designsystem.components.toast.linkMindSnackBar import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.sopt.clip.cliplink.ClipLinkViewModel @@ -26,22 +26,17 @@ class ClipChangeFragment : BindingFragment({ FragmentClipChangeBinding.inflate(it) }) { private lateinit var clipAdapter: ClipChangeAdapter private val viewModel: ClipLinkViewModel by activityViewModels() - //TODO. 현재 id = 리스트에서 빼던지, id받아와서 리사이클러뷰 아이템에서 뺴기 - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val args: ClipChangeFragmentArgs by navArgs() - getCategoryAll() collectClipState(args) initCloseButtonClickListener() } private fun getCategoryAll() { - viewModel.getCategoryAll() - + viewModel.getCategoryAll() } private fun collectClipState(args: ClipChangeFragmentArgs) { @@ -69,7 +64,6 @@ class ClipChangeFragment : binding.btnClipChangeSelectNext.state = if (clipAdapter.selectedPosition != -1) LinkMindButtonState.ENABLE else LinkMindButtonState.DISABLE binding.rvClipChangeSelect.adapter = clipAdapter binding.rvClipChangeSelect.itemAnimator = null - initNextButtonClickListener() } private fun handleClipClick( @@ -87,17 +81,19 @@ class ClipChangeFragment : list.onEach { it.isSelected = false } binding.btnClipChangeSelectNext.state = LinkMindButtonState.DISABLE } + + initNextButtonClickListener(toastId, newClipId) } - private fun initNextButtonClickListener() { + private fun initNextButtonClickListener(toastId: Long, newClipId: Long) { binding.btnClipChangeSelectNext.btnClick { - Log.d("asdasd", "next") + viewModel.patchLinkCategory(toastId = toastId, categoryId = newClipId) + findNavController().popBackStack() } } private fun initCloseButtonClickListener() { binding.ivClipChangeClose.onThrottleClick { - Log.d("asdasd", "close") findNavController().popBackStack() } } diff --git a/feature/clip/src/main/java/org/sopt/clip/ClipChangeViewHolder.kt b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeViewHolder.kt similarity index 98% rename from feature/clip/src/main/java/org/sopt/clip/ClipChangeViewHolder.kt rename to feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeViewHolder.kt index 1a3b422a..ce0e7759 100644 --- a/feature/clip/src/main/java/org/sopt/clip/ClipChangeViewHolder.kt +++ b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeViewHolder.kt @@ -1,4 +1,4 @@ -package org.sopt.clip +package org.sopt.clip.clipchange import android.content.Context import androidx.recyclerview.widget.RecyclerView diff --git a/feature/clip/src/main/java/org/sopt/clip/clipedit/ClipEditFragment.kt b/feature/clip/src/main/java/org/sopt/clip/clipedit/ClipEditFragment.kt index df9e19c3..4d6978c4 100644 --- a/feature/clip/src/main/java/org/sopt/clip/clipedit/ClipEditFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/clipedit/ClipEditFragment.kt @@ -119,7 +119,7 @@ class ClipEditFragment : BindingFragment({ FragmentClip } } - fun editCategoryTitle() { + private fun editCategoryTitle() { viewModel.editTitleState.flowWithLifecycle(viewLifeCycle).onEach { state -> when (state) { is UiState.Success -> { diff --git a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt index 927a9305..49914d22 100644 --- a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkFragment.kt @@ -1,11 +1,10 @@ package org.sopt.clip.cliplink import android.os.Bundle -import android.util.Log import android.view.View import android.widget.TextView import androidx.core.view.isVisible -import androidx.fragment.app.viewModels +import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -17,7 +16,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.sopt.clip.DeleteLinkBottomSheetFragment import org.sopt.clip.R -import org.sopt.clip.clip.ClipFragmentDirections import org.sopt.clip.databinding.FragmentClipLinkBinding import org.sopt.common.util.delSpace import org.sopt.model.category.Category @@ -33,7 +31,7 @@ import java.nio.charset.StandardCharsets @AndroidEntryPoint class ClipLinkFragment : BindingFragment({ FragmentClipLinkBinding.inflate(it) }) { - private val viewModel: ClipLinkViewModel by viewModels() + private val viewModel: ClipLinkViewModel by activityViewModels() private lateinit var clipLinkAdapter: ClipLinkAdapter private var isDataNull: Boolean = true override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -92,6 +90,7 @@ class ClipLinkFragment : BindingFragment({ FragmentClip updateLinkView() updateAllCount() updateLinkTitle(categoryId) + updateLinkTitles() onClickBackButton() } @@ -148,6 +147,18 @@ class ClipLinkFragment : BindingFragment({ FragmentClip }.launchIn(viewLifeCycleScope) } + private fun updateLinkTitles() { + viewModel.patchLinkCategory.flowWithLifecycle(viewLifeCycle).onEach { state -> + when (state) { + is UiState.Success -> { + requireContext().linkMindSnackBar(binding.vSnack, "클립 이동 완료", true) + } + + else -> {} + } + }.launchIn(viewLifeCycleScope) + } + private fun updateLinkDelete(categoryId: Long) { viewModel.deleteState.flowWithLifecycle(viewLifeCycle).onEach { state -> when (state) { @@ -257,8 +268,8 @@ class ClipLinkFragment : BindingFragment({ FragmentClip DeleteLinkBottomSheetFragment.newInstance( clipId, isFullClipSize, - {requireContext().linkMindSnackBar(binding.vSnack, "클립 하나임", true)}, - handleDeleteButton = { + { requireContext().linkMindSnackBar(binding.vSnack, "클립 하나임", true) }, + handleDeleteButton = { viewModel.deleteLink(linkDTO.toastId) }, handleChangeButton = { diff --git a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkViewModel.kt b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkViewModel.kt index db35b171..9d90c454 100644 --- a/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkViewModel.kt +++ b/feature/clip/src/main/java/org/sopt/clip/cliplink/ClipLinkViewModel.kt @@ -12,6 +12,7 @@ import org.sopt.clip.SelectedToggle import org.sopt.domain.category.category.usecase.GetCategoryAllUseCase import org.sopt.domain.category.category.usecase.GetCategoryLinkUseCase import org.sopt.domain.link.usecase.DeleteLinkUseCase +import org.sopt.domain.link.usecase.PatchLinkCategoryUseCase import org.sopt.domain.link.usecase.PatchLinkTitleUseCase import org.sopt.model.category.Category import org.sopt.model.category.CategoryLink @@ -24,6 +25,7 @@ class ClipLinkViewModel @Inject constructor( private val deleteLinkUseCase: DeleteLinkUseCase, private val patchLinkTitleUseCase: PatchLinkTitleUseCase, private val getCategoryAll: GetCategoryAllUseCase, + private val patchLinkCategoryUseCase: PatchLinkCategoryUseCase, ) : ViewModel() { private val _linkState = MutableStateFlow>>(UiState.Empty) val linkState: StateFlow>> = _linkState.asStateFlow() @@ -40,6 +42,9 @@ class ClipLinkViewModel @Inject constructor( private val _categoryState = MutableStateFlow>>(UiState.Empty) val categoryState: StateFlow>> = _categoryState.asStateFlow() + private val _patchLinkCategory = MutableStateFlow>(UiState.Empty) + val patchLinkCategory: StateFlow> = _patchLinkCategory.asStateFlow() + var toggleSelectedPast: SelectedToggle = SelectedToggle.ALL fun deleteLink(toastId: Long) = viewModelScope.launch { deleteLinkUseCase.invoke(param = DeleteLinkUseCase.Param(toastId = toastId)).onSuccess { @@ -86,11 +91,16 @@ class ClipLinkViewModel @Inject constructor( } } - fun patchClipChange() { - + fun patchLinkCategory(toastId: Long, categoryId: Long) = viewModelScope.launch { + patchLinkCategoryUseCase(param = PatchLinkCategoryUseCase.Param(toastId = toastId, categoryId = categoryId)).onSuccess { + _patchLinkCategory.emit(UiState.Success(it)) + }.onFailure { + _patchLinkCategory.emit(UiState.Failure("fail")) + } } fun initState() { _linkState.value = UiState.Empty + _patchLinkCategory.value = UiState.Empty } } diff --git a/feature/clip/src/main/res/layout/fragment_clip_change.xml b/feature/clip/src/main/res/layout/fragment_clip_change.xml index 8b4fe0fe..7ffaf9fa 100644 --- a/feature/clip/src/main/res/layout/fragment_clip_change.xml +++ b/feature/clip/src/main/res/layout/fragment_clip_change.xml @@ -58,4 +58,10 @@ android:layout_marginBottom="12dp" app:fullWidthBtnText="다음" app:layout_constraintBottom_toBottomOf="parent" /> + + diff --git a/feature/clip/src/main/res/navigation/nav_graph_clip.xml b/feature/clip/src/main/res/navigation/nav_graph_clip.xml index 7e967fa0..b19b9013 100644 --- a/feature/clip/src/main/res/navigation/nav_graph_clip.xml +++ b/feature/clip/src/main/res/navigation/nav_graph_clip.xml @@ -67,7 +67,7 @@ Date: Wed, 6 Nov 2024 04:44:21 +0900 Subject: [PATCH 4/5] [chore] #182 ktlint --- .../sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt | 2 +- .../main/java/org/sopt/clip/clipchange/ClipChangeFragment.kt | 1 - .../main/java/org/sopt/timer/settimer/SetTimerViewModel.kt | 4 ++-- .../org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt | 2 +- .../sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt | 2 +- .../sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/data-remote/link/src/main/java/org/sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt b/data-remote/link/src/main/java/org/sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt index 2ce9094b..9f7cb9ad 100644 --- a/data-remote/link/src/main/java/org/sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt +++ b/data-remote/link/src/main/java/org/sopt/remote/link/datasource/RemoteLinkDataSourceImpl.kt @@ -43,7 +43,7 @@ class RemoteLinkDataSourceImpl @Inject constructor( linkService.patchToastCategory( RequestPatchCategoryDto( toastId = toastId, - categoryId = categoryId + categoryId = categoryId, ), ).data!!.categoryId } diff --git a/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeFragment.kt b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeFragment.kt index ede46c5d..d6b364a9 100644 --- a/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeFragment.kt +++ b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeFragment.kt @@ -8,7 +8,6 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint import designsystem.components.button.state.LinkMindButtonState -import designsystem.components.toast.linkMindSnackBar import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.sopt.clip.cliplink.ClipLinkViewModel diff --git a/feature/timer/src/main/java/org/sopt/timer/settimer/SetTimerViewModel.kt b/feature/timer/src/main/java/org/sopt/timer/settimer/SetTimerViewModel.kt index 58f423e7..1c664e5c 100644 --- a/feature/timer/src/main/java/org/sopt/timer/settimer/SetTimerViewModel.kt +++ b/feature/timer/src/main/java/org/sopt/timer/settimer/SetTimerViewModel.kt @@ -10,10 +10,10 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import org.sopt.domain.category.category.usecase.GetCategoryAllUseCase import org.sopt.model.category.Category -import org.sopt.model.timer.Repeat import org.sopt.model.timer.Clip -import org.sopt.timer.model.TimePicker +import org.sopt.model.timer.Repeat import org.sopt.model.timer.toUiModel +import org.sopt.timer.model.TimePicker import org.sopt.timer.usecase.FormatRepeatListToIntList import org.sopt.timer.usecase.FormatRepeatListToStringList import org.sopt.timer.usecase.PatchTimerUseCase diff --git a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt index 5f7ff6bf..fa700257 100644 --- a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt +++ b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectAdapter.kt @@ -4,8 +4,8 @@ import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter -import org.sopt.timer.databinding.ItemTimerClipSelectBinding import org.sopt.model.timer.Clip +import org.sopt.timer.databinding.ItemTimerClipSelectBinding import org.sopt.ui.view.ItemDiffCallback class ClipSelectAdapter( diff --git a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt index a405eae1..a894ded9 100644 --- a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt +++ b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/ClipSelectViewHolder.kt @@ -3,8 +3,8 @@ package org.sopt.timer.settimer.clipselect import android.content.Context import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView -import org.sopt.timer.databinding.ItemTimerClipSelectBinding import org.sopt.model.timer.Clip +import org.sopt.timer.databinding.ItemTimerClipSelectBinding import org.sopt.ui.view.onThrottleClick class ClipSelectViewHolder( diff --git a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt index c192490e..e7563ccd 100644 --- a/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt +++ b/feature/timer/src/main/java/org/sopt/timer/settimer/clipselect/TimerClipSelectFragment.kt @@ -10,9 +10,9 @@ import designsystem.components.button.state.LinkMindButtonState import designsystem.components.dialog.LinkMindDialog import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import org.sopt.model.timer.Clip import org.sopt.timer.R import org.sopt.timer.databinding.FragmentTimerClipSelectBinding -import org.sopt.model.timer.Clip import org.sopt.timer.settimer.SetTimerViewModel import org.sopt.ui.base.BindingFragment import org.sopt.ui.fragment.viewLifeCycle From c4856678e1888aa31f50c9518b865f2ca2535d43 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:43:37 +0900 Subject: [PATCH 5/5] [refactor] #182 refactor --- .../main/java/org/sopt/clip/clipchange/ClipChangeAdapter.kt | 2 +- feature/home/src/main/java/org/sopt/home/HomeFragment.kt | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeAdapter.kt b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeAdapter.kt index c82fcaad..b33531a2 100644 --- a/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeAdapter.kt +++ b/feature/clip/src/main/java/org/sopt/clip/clipchange/ClipChangeAdapter.kt @@ -55,7 +55,7 @@ class ClipChangeAdapter( companion object { private val DiffUtil = ItemDiffCallback( - onItemsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old.id == new.id }, onContentsTheSame = { old, new -> old == new }, ) } diff --git a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt index c174e0d2..2b58bee4 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt @@ -5,9 +5,6 @@ import android.view.View import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint -import designsystem.components.bottomsheet.BottomSheetType -import designsystem.components.bottomsheet.LinkMindBottomSheet -import designsystem.components.toast.linkMindSnackBar import org.orbitmvi.orbit.viewmodel.observe import org.sopt.common.util.delSpace import org.sopt.home.adapter.HomeClipAdapter @@ -67,6 +64,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. is HomeSideEffect.NavigateClipLink -> navigateToDestination( "featureSaveLink://ClipLinkFragment/${viewModel.container.stateFlow.value.categoryId}/${viewModel.container.stateFlow.value.categoryName}", ) + is HomeSideEffect.NavigateSaveLink -> navigateToDestinationWithoutAnim("featureSaveLink://saveLinkFragment?clipboardLink=") is HomeSideEffect.NavigateWebView -> { val encodedURL = URLEncoder.encode(viewModel.container.stateFlow.value.url, StandardCharsets.UTF_8.toString()) @@ -74,6 +72,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. "featureSaveLink://webViewFragment/${0}/${false}/${false}/$encodedURL", ) } + is HomeSideEffect.NavigateAllClip -> navigateToDestinationWithoutAnim("featureSaveLink://ClipLinkFragment/0/전체 클립") is HomeSideEffect.ShowPopupInfo -> showPopupInfo(viewModel.container.stateFlow.value.popupList) is HomeSideEffect.ShowUpdateDialog -> showUpdateDialog(viewModel.container.stateFlow.value.marketUpdate)