From 6096f7c6f4225353b1b1f793f40080e933a2e62f Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 03:38:34 +0900 Subject: [PATCH 01/19] change name --- presentation/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index a4a7cdb..08c8828 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -18,7 +18,7 @@ 리듬 기록 - 학습 + 과제 마이 %d단계 리듬 From bd8ee78dbfc0a075f44b6b5bd79809999e4e9cba Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 15:10:25 +0900 Subject: [PATCH 02/19] [FEAT/#57] feat custom toggle --- .../res/drawable/shape_toggle_background.xml | 7 +++ .../res/drawable/shape_toggle_selected.xml | 5 ++ .../main/res/layout/item_custom_toggle.xml | 51 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 presentation/src/main/res/drawable/shape_toggle_background.xml create mode 100644 presentation/src/main/res/drawable/shape_toggle_selected.xml create mode 100644 presentation/src/main/res/layout/item_custom_toggle.xml diff --git a/presentation/src/main/res/drawable/shape_toggle_background.xml b/presentation/src/main/res/drawable/shape_toggle_background.xml new file mode 100644 index 0000000..433418e --- /dev/null +++ b/presentation/src/main/res/drawable/shape_toggle_background.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_toggle_selected.xml b/presentation/src/main/res/drawable/shape_toggle_selected.xml new file mode 100644 index 0000000..7797603 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_toggle_selected.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/item_custom_toggle.xml b/presentation/src/main/res/layout/item_custom_toggle.xml new file mode 100644 index 0000000..954a6f9 --- /dev/null +++ b/presentation/src/main/res/layout/item_custom_toggle.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file From 111415e049e1b292c6395fa6a625cf2b4303719f Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 15:26:18 +0900 Subject: [PATCH 03/19] [FEAT/#57] feat custom toggle clickListener --- .../presentation/main/study/StudyFragment.kt | 40 +++++------ .../src/main/res/layout/fragment_study.xml | 68 +++++++++++++------ .../main/res/layout/item_custom_toggle.xml | 4 +- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index e6d51f7..99dbe63 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -1,10 +1,11 @@ package com.kkkk.presentation.main.study +import android.annotation.SuppressLint import android.os.Bundle import android.view.View -import com.google.android.material.tabs.TabLayoutMediator +import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toDrawable import com.kkkk.core.base.BaseFragment -import com.kkkk.core.extension.colorOf import com.kkkk.core.extension.setStatusBarColor import dagger.hilt.android.AndroidEntryPoint import kr.genti.presentation.R @@ -20,26 +21,27 @@ class StudyFragment : BaseFragment(R.layout.fragment_study ) { super.onViewCreated(view, savedInstanceState) - setTabLayout() - setViewPager() - setStatusBarColor(R.color.white) + setStatusBarColor(R.color.gray_100) + setToggleClickListener() } - private fun setTabLayout() { - binding.tabStudy.apply { - for (tabName in tabTextList) { - val tab = this.newTab() - tab.text = tabName - this.addTab(tab) + @SuppressLint("ResourceAsColor") + private fun setToggleClickListener() { + binding.itemToggle.tvTeacher.setOnClickListener { + with(binding.itemToggle) { + tvTeacher.setBackgroundResource(R.drawable.shape_toggle_selected) + tvTeacher.setTextColor(ContextCompat.getColor(requireContext(), R.color.black)) + tvStudent.background = null + tvStudent.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) + } + } + binding.itemToggle.tvStudent.setOnClickListener { + with(binding.itemToggle) { + tvStudent.setBackgroundResource(R.drawable.shape_toggle_selected) + tvStudent.setTextColor(ContextCompat.getColor(requireContext(), R.color.black)) + tvTeacher.background = null + tvTeacher.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) } } - } - - private fun setViewPager() { - binding.vpStudy.adapter = StudyViewPagerAdapter(requireActivity()) - binding.vpStudy.isUserInputEnabled = false - TabLayoutMediator(binding.tabStudy, binding.vpStudy) { tab, pos -> - tab.text = tabTextList[pos] - }.attach() } } diff --git a/presentation/src/main/res/layout/fragment_study.xml b/presentation/src/main/res/layout/fragment_study.xml index c7b3f30..5b43b7c 100644 --- a/presentation/src/main/res/layout/fragment_study.xml +++ b/presentation/src/main/res/layout/fragment_study.xml @@ -1,6 +1,7 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:margin="http://schemas.android.com/tools"> @@ -9,33 +10,58 @@ - - - + + + + + app:layout_constraintTop_toBottomOf="@id/tv_title"> + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/item_custom_toggle.xml b/presentation/src/main/res/layout/item_custom_toggle.xml index 954a6f9..32f5bc7 100644 --- a/presentation/src/main/res/layout/item_custom_toggle.xml +++ b/presentation/src/main/res/layout/item_custom_toggle.xml @@ -25,7 +25,6 @@ style="@style/TextAppearance.Stempo.Body3" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_marginEnd="6dp" android:background="@drawable/shape_toggle_selected" android:gravity="center" android:paddingVertical="3dp" @@ -33,14 +32,13 @@ android:paddingLeft="15dp" android:paddingEnd="14dp" android:text="본인" - android:textColor="@color/white" /> + android:textColor="@color/black" /> Date: Sun, 22 Sep 2024 16:34:30 +0900 Subject: [PATCH 04/19] [FEAT/#57] delete unused file --- .../main/study/StudyExerciseFragment.kt | 79 ------------- .../main/study/StudyInformationFragment.kt | 80 ------------- .../main/study/StudyViewPagerAdapter.kt | 16 --- .../res/layout/fragment_study_exercise.xml | 106 ------------------ .../res/layout/fragment_study_information.xml | 104 ----------------- 5 files changed, 385 deletions(-) delete mode 100644 presentation/src/main/java/com/kkkk/presentation/main/study/StudyExerciseFragment.kt delete mode 100644 presentation/src/main/java/com/kkkk/presentation/main/study/StudyInformationFragment.kt delete mode 100644 presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewPagerAdapter.kt delete mode 100644 presentation/src/main/res/layout/fragment_study_exercise.xml delete mode 100644 presentation/src/main/res/layout/fragment_study_information.xml diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyExerciseFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyExerciseFragment.kt deleted file mode 100644 index 5a59330..0000000 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyExerciseFragment.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.kkkk.presentation.main.study - -import android.os.Bundle -import android.view.View -import androidx.core.view.isVisible -import androidx.fragment.app.viewModels -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import coil.load -import com.kkkk.core.base.BaseFragment -import com.kkkk.domain.entity.response.StudyModel -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kr.genti.presentation.R -import kr.genti.presentation.databinding.FragmentStudyExerciseBinding - -@AndroidEntryPoint -class StudyExerciseFragment : - BaseFragment(R.layout.fragment_study_exercise) { - private val viewModel by viewModels() - - private val items by lazy { - listOf( - binding.itemStudyExercise1, - binding.itemStudyExercise2 - ) - } - - override fun onViewCreated( - view: View, - savedInstanceState: Bundle?, - ) { - super.onViewCreated(view, savedInstanceState) - - setObserver() - setButtonClickListeners() - } - - private fun setObserver() { - viewModel.videoState.flowWithLifecycle(lifecycle).onEach { state -> - with(binding) { - btnStudyExerciseBefore.visibility = - if (state.hasPrevious) View.VISIBLE else View.INVISIBLE - - btnStudyExerciseNext.visibility = - if (state.hasNext) View.VISIBLE else View.INVISIBLE - - updateItems(state.items) - } - }.launchIn(lifecycleScope) - } - - private fun updateItems(stateItems: List) { - items.forEachIndexed { index, item -> - if (index < stateItems.size) { - item.root.isVisible = true - with(item) { - tvStudyExerciseTitle.text = stateItems[index].title - tvStudyExerciseContent.text = stateItems[index].content - ivStudyExerciseThumbnail.load( - stateItems[index].thumbnailUrl ?: R.drawable.img_default_content - ) - } - } else { - item.root.visibility = View.INVISIBLE - } - } - } - - private fun setButtonClickListeners() { - binding.btnStudyExerciseBefore.setOnClickListener { - viewModel.getVideos(-1) - } - binding.btnStudyExerciseNext.setOnClickListener { - viewModel.getVideos(1) - } - } -} diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyInformationFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyInformationFragment.kt deleted file mode 100644 index 35e591d..0000000 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyInformationFragment.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.kkkk.presentation.main.study - -import android.os.Bundle -import android.view.View -import androidx.core.view.isVisible -import androidx.fragment.app.viewModels -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import coil.load -import com.kkkk.core.base.BaseFragment -import com.kkkk.domain.entity.response.StudyModel -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kr.genti.presentation.R -import kr.genti.presentation.databinding.FragmentStudyInformationBinding - -@AndroidEntryPoint -class StudyInformationFragment : - BaseFragment(R.layout.fragment_study_information) { - private val viewModel by viewModels() - - private val items by lazy { - listOf( - binding.itemStudyInformation1, - binding.itemStudyInformation2, - binding.itemStudyInformation3 - ) - } - - override fun onViewCreated( - view: View, - savedInstanceState: Bundle?, - ) { - super.onViewCreated(view, savedInstanceState) - - setObserver() - setButtonClickListeners() - } - - private fun setObserver() { - viewModel.articleState.flowWithLifecycle(lifecycle).onEach { state -> - with(binding) { - btnStudyInformationBefore.visibility = - if (state.hasPrevious) View.VISIBLE else View.INVISIBLE - - btnStudyInformationNext.visibility = - if (state.hasNext) View.VISIBLE else View.INVISIBLE - - updateItems(state.items) - } - }.launchIn(lifecycleScope) - } - - private fun updateItems(stateItems: List) { - items.forEachIndexed { index, item -> - if (index < stateItems.size) { - item.root.isVisible = true - with(item) { - tvStudyInformationTitle.text = stateItems[index].title - tvStudyExerciseContent.text = stateItems[index].content - ivStudyInformationThumbnail.load( - stateItems[index].thumbnailUrl ?: R.drawable.img_default_content - ) - } - } else { - item.root.visibility = View.INVISIBLE - } - } - } - - private fun setButtonClickListeners() { - binding.btnStudyInformationBefore.setOnClickListener { - viewModel.getArticles(-1) - } - binding.btnStudyInformationNext.setOnClickListener { - viewModel.getArticles(1) - } - } -} \ No newline at end of file diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewPagerAdapter.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewPagerAdapter.kt deleted file mode 100644 index cb8bb0d..0000000 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewPagerAdapter.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.kkkk.presentation.main.study - -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity -import androidx.viewpager2.adapter.FragmentStateAdapter - -class StudyViewPagerAdapter(fragment: FragmentActivity) : FragmentStateAdapter(fragment) { - override fun getItemCount(): Int = 2 - - override fun createFragment(position: Int): Fragment { - return when (position) { - 0 -> StudyExerciseFragment() - else -> StudyInformationFragment() - } - } -} \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_study_exercise.xml b/presentation/src/main/res/layout/fragment_study_exercise.xml deleted file mode 100644 index 3e64f18..0000000 --- a/presentation/src/main/res/layout/fragment_study_exercise.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_study_information.xml b/presentation/src/main/res/layout/fragment_study_information.xml deleted file mode 100644 index 76070d6..0000000 --- a/presentation/src/main/res/layout/fragment_study_information.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From fd24b3b1c4f2a0177ae242bbc78d38fec81d9342 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 16:34:40 +0900 Subject: [PATCH 05/19] [FEAT/#57] feat study ui --- .../presentation/main/study/StudyFragment.kt | 94 ++++++++++-- .../presentation/main/study/StudyViewModel.kt | 42 ++---- .../src/main/res/drawable/img_study_empty.xml | 43 ++++++ .../src/main/res/layout/fragment_study.xml | 135 +++++++++++++++++- presentation/src/main/res/values/strings.xml | 3 + 5 files changed, 268 insertions(+), 49 deletions(-) create mode 100644 presentation/src/main/res/drawable/img_study_empty.xml diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 99dbe63..86e4da7 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -4,16 +4,21 @@ import android.annotation.SuppressLint import android.os.Bundle import android.view.View import androidx.core.content.ContextCompat -import androidx.core.graphics.drawable.toDrawable +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.kkkk.core.base.BaseFragment import com.kkkk.core.extension.setStatusBarColor import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kr.genti.presentation.R import kr.genti.presentation.databinding.FragmentStudyBinding @AndroidEntryPoint class StudyFragment : BaseFragment(R.layout.fragment_study) { - private val tabTextList = listOf("재활운동", "정보") + private val viewModel by activityViewModels() override fun onViewCreated( view: View, @@ -23,24 +28,85 @@ class StudyFragment : BaseFragment(R.layout.fragment_study setStatusBarColor(R.color.gray_100) setToggleClickListener() + viewModel.typeIsMe.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { isMe -> + setToggleState(isMe) + }.launchIn(lifecycleScope) } @SuppressLint("ResourceAsColor") private fun setToggleClickListener() { + binding.itemToggle.tvStudent.setOnClickListener { + viewModel.setTypeIsMe(true) + } binding.itemToggle.tvTeacher.setOnClickListener { - with(binding.itemToggle) { - tvTeacher.setBackgroundResource(R.drawable.shape_toggle_selected) - tvTeacher.setTextColor(ContextCompat.getColor(requireContext(), R.color.black)) - tvStudent.background = null - tvStudent.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) - } + viewModel.setTypeIsMe(false) } - binding.itemToggle.tvStudent.setOnClickListener { - with(binding.itemToggle) { - tvStudent.setBackgroundResource(R.drawable.shape_toggle_selected) - tvStudent.setTextColor(ContextCompat.getColor(requireContext(), R.color.black)) - tvTeacher.background = null - tvTeacher.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) + } + + private fun setToggleState(isMe: Boolean) { + when (isMe) { + true -> { + with(binding) { + with(itemToggle) { + tvStudent.setBackgroundResource(R.drawable.shape_toggle_selected) + tvStudent.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.black + ) + ) + tvTeacher.background = null + tvTeacher.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.white + ) + ) + } + + layoutMyExercise.visibility = View.VISIBLE + layoutTeacherExercise.visibility = View.GONE + + if (viewModel.studyList.value.isEmpty()) { + ivMyExerciseEmpty.visibility = View.VISIBLE + layoutMyExerciseValid.visibility = View.GONE + } else { + ivMyExerciseEmpty.visibility = View.GONE + layoutMyExerciseValid.visibility = View.VISIBLE + } + } + } + + false -> { + with(binding) { + with(itemToggle) { + tvTeacher.setBackgroundResource(R.drawable.shape_toggle_selected) + tvTeacher.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.black + ) + ) + tvStudent.background = null + tvStudent.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.white + ) + ) + } + + layoutMyExercise.visibility = View.GONE + layoutTeacherExercise.visibility = View.VISIBLE + + if (viewModel.studyList.value.isEmpty()) { + ivTeacherExerciseEmpty.visibility = View.VISIBLE + rvTeacherExercise.visibility = View.GONE + } else { + ivTeacherExerciseEmpty.visibility = View.GONE + rvTeacherExercise.visibility = View.VISIBLE + } + } } } } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt index 7bd5a1b..4b42da5 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt @@ -1,52 +1,30 @@ package com.kkkk.presentation.main.study +import android.util.Log import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import com.kkkk.domain.entity.response.StudyModel import com.kkkk.domain.repository.StudyRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import timber.log.Timber import javax.inject.Inject @HiltViewModel class StudyViewModel @Inject constructor( private val studyRepository: StudyRepository, ) : ViewModel() { - private val _videoState = MutableStateFlow(StudyModel()) - val videoState: StateFlow - get() = _videoState + // 추후 과제 리스트 관련으로 사용 + private val _studyList = MutableStateFlow>(emptyList()) + val studyList: StateFlow> = _studyList - private val _articleState = MutableStateFlow(StudyModel()) - val articleState: StateFlow - get() = _articleState + private val _typeIsMe = MutableStateFlow(true) + val typeIsMe: StateFlow = _typeIsMe - init { - getVideos() - getArticles() + fun setTypeIsMe(isMe: Boolean) { + _typeIsMe.value = isMe } - fun getVideos(value: Int = 0) { - viewModelScope.launch { - studyRepository.getVideos( - page = _videoState.value.currentPage + value, - size = 2 - ).onSuccess { - _videoState.value = it - }.onFailure(Timber::e) - } - } - - fun getArticles(value: Int = 0) { - viewModelScope.launch { - studyRepository.getArticles( - page = _articleState.value.currentPage + value, - size = 3 - ).onSuccess { - _articleState.value = it - }.onFailure(Timber::e) - } + fun addItems(items: List) { + _studyList.value += items } } diff --git a/presentation/src/main/res/drawable/img_study_empty.xml b/presentation/src/main/res/drawable/img_study_empty.xml new file mode 100644 index 0000000..11ad419 --- /dev/null +++ b/presentation/src/main/res/drawable/img_study_empty.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/fragment_study.xml b/presentation/src/main/res/layout/fragment_study.xml index 5b43b7c..ff7ac94 100644 --- a/presentation/src/main/res/layout/fragment_study.xml +++ b/presentation/src/main/res/layout/fragment_study.xml @@ -1,17 +1,21 @@ + xmlns:margin="http://schemas.android.com/tools" + xmlns:tools="http://schemas.android.com/tools"> + + android:background="@color/gray_100" + android:paddingHorizontal="16dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 08c8828..1683355 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -54,4 +54,7 @@ 불편한 점 얘기하기 (음성) 건의하기 + 진행해야 할 과제에요! + 입력한 과제에요! + \ No newline at end of file From b7fbdcea54634b2728dfbf6a82df6032865cd0e8 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 16:37:52 +0900 Subject: [PATCH 06/19] [FEAT/#57] feat dummy button clickListener --- .../kkkk/presentation/main/study/StudyFragment.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 86e4da7..ab23974 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -27,7 +27,12 @@ class StudyFragment : BaseFragment(R.layout.fragment_study super.onViewCreated(view, savedInstanceState) setStatusBarColor(R.color.gray_100) + observeTypeIsMe() setToggleClickListener() + setAddStudyButtonClickListener() + } + + private fun observeTypeIsMe() { viewModel.typeIsMe.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { isMe -> setToggleState(isMe) }.launchIn(lifecycleScope) @@ -43,6 +48,13 @@ class StudyFragment : BaseFragment(R.layout.fragment_study } } + private fun setAddStudyButtonClickListener() { + binding.btnTeacherExercise.setOnClickListener { + viewModel.addItems(listOf(1, 2, 3)) + setToggleState(false) + } + } + private fun setToggleState(isMe: Boolean) { when (isMe) { true -> { From 44cb7ed75a62624fe76fb045dbbff99192a92a02 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 17:03:35 +0900 Subject: [PATCH 07/19] [FEAT/#57] delete unused xml --- .../main/res/layout/item_study_exercise.xml | 71 ------------------- .../res/layout/item_study_information.xml | 55 -------------- 2 files changed, 126 deletions(-) delete mode 100644 presentation/src/main/res/layout/item_study_exercise.xml delete mode 100644 presentation/src/main/res/layout/item_study_information.xml diff --git a/presentation/src/main/res/layout/item_study_exercise.xml b/presentation/src/main/res/layout/item_study_exercise.xml deleted file mode 100644 index 7f95a3a..0000000 --- a/presentation/src/main/res/layout/item_study_exercise.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/presentation/src/main/res/layout/item_study_information.xml b/presentation/src/main/res/layout/item_study_information.xml deleted file mode 100644 index 662dc76..0000000 --- a/presentation/src/main/res/layout/item_study_information.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - \ No newline at end of file From 95a45cc9ced8de82849163390958c852fc13ade9 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 17:03:42 +0900 Subject: [PATCH 08/19] [FEAT/#57] add icon --- presentation/src/main/res/drawable/ic_cancel.xml | 13 +++++++++++++ .../main/res/drawable/ic_checkbox_checked.xml | 16 ++++++++++++++++ .../main/res/drawable/ic_checkbox_unchecked.xml | 16 ++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 presentation/src/main/res/drawable/ic_cancel.xml create mode 100644 presentation/src/main/res/drawable/ic_checkbox_checked.xml create mode 100644 presentation/src/main/res/drawable/ic_checkbox_unchecked.xml diff --git a/presentation/src/main/res/drawable/ic_cancel.xml b/presentation/src/main/res/drawable/ic_cancel.xml new file mode 100644 index 0000000..5e5b7c6 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_cancel.xml @@ -0,0 +1,13 @@ + + + diff --git a/presentation/src/main/res/drawable/ic_checkbox_checked.xml b/presentation/src/main/res/drawable/ic_checkbox_checked.xml new file mode 100644 index 0000000..b76d2e5 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_checkbox_checked.xml @@ -0,0 +1,16 @@ + + + + diff --git a/presentation/src/main/res/drawable/ic_checkbox_unchecked.xml b/presentation/src/main/res/drawable/ic_checkbox_unchecked.xml new file mode 100644 index 0000000..3f530e1 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_checkbox_unchecked.xml @@ -0,0 +1,16 @@ + + + + From 3de7f0ef8a9f1f3f6f4d2e1a55d8dceba84ff55a Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 17:03:51 +0900 Subject: [PATCH 09/19] [FEAT/#57] feat study item --- .../res/layout/item_study_check_string.xml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 presentation/src/main/res/layout/item_study_check_string.xml diff --git a/presentation/src/main/res/layout/item_study_check_string.xml b/presentation/src/main/res/layout/item_study_check_string.xml new file mode 100644 index 0000000..b296c06 --- /dev/null +++ b/presentation/src/main/res/layout/item_study_check_string.xml @@ -0,0 +1,40 @@ + + + + + + + + + + From 474c4990f5e770a366f7a3f84795ec5133acb6c9 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 20:40:11 +0900 Subject: [PATCH 10/19] [FEAT/#57] feat study item ui and get api --- .../kkkk/data/dataSource/StudyDataSource.kt | 10 +----- .../dataSourceImpl/StudyDataSourceImpl.kt | 15 ++------ .../com/kkkk/data/dto/response/StudyDto.kt | 21 ++++------- .../repositoryImpl/StudyRepositoryImpl.kt | 21 ++--------- .../com/kkkk/data/service/StudyService.kt | 12 ++----- .../kkkk/domain/entity/response/StudyModel.kt | 6 ++-- .../kkkk/domain/repository/StudyRepository.kt | 7 +--- .../presentation/main/study/StudyAdapter.kt | 35 ++++++++++++++++++ .../presentation/main/study/StudyFragment.kt | 36 ++++++++++++++++++- .../main/study/StudyViewHolder.kt | 28 +++++++++++++++ .../presentation/main/study/StudyViewModel.kt | 24 +++++++++---- .../src/main/res/layout/fragment_study.xml | 11 ++++-- .../res/layout/item_study_check_string.xml | 5 ++- 13 files changed, 146 insertions(+), 85 deletions(-) create mode 100644 presentation/src/main/java/com/kkkk/presentation/main/study/StudyAdapter.kt create mode 100644 presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt diff --git a/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt b/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt index 62d2f77..f371ca1 100644 --- a/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt +++ b/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt @@ -4,13 +4,5 @@ import com.kkkk.data.dto.BaseResponse import com.kkkk.data.dto.response.StudyDto interface StudyDataSource { - suspend fun getVideos( - page: Int, - size: Int - ): BaseResponse - - suspend fun getArticles( - page: Int, - size: Int - ): BaseResponse + suspend fun getHomeworks(page: Int, size: Int): BaseResponse } diff --git a/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt b/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt index f533af1..eac5e53 100644 --- a/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt +++ b/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt @@ -9,17 +9,6 @@ import javax.inject.Inject data class StudyDataSourceImpl @Inject constructor( private val studyService: StudyService, ) : StudyDataSource { - override suspend fun getVideos( - page: Int, - size: Int, - ): BaseResponse = studyService.getVideos( - page = page, - size = size - ) - - override suspend fun getArticles(page: Int, size: Int): BaseResponse = - studyService.getArticles( - page = page, - size = size - ) + override suspend fun getHomeworks(page: Int, size: Int): BaseResponse = + studyService.getHomeworks(page, size) } diff --git a/data/src/main/java/com/kkkk/data/dto/response/StudyDto.kt b/data/src/main/java/com/kkkk/data/dto/response/StudyDto.kt index 629a92b..1ee3fad 100644 --- a/data/src/main/java/com/kkkk/data/dto/response/StudyDto.kt +++ b/data/src/main/java/com/kkkk/data/dto/response/StudyDto.kt @@ -1,6 +1,5 @@ package com.kkkk.data.dto.response -import com.kkkk.data.R import com.kkkk.domain.entity.response.StudyModel import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -26,21 +25,15 @@ data class StudyDto( data class StudyItemDto( @SerialName("id") val id: Int, - @SerialName("title") - val title: String, - @SerialName("content") - val content: String, - @SerialName("thumbnailUrl") - val thumbnailUrl: String?, - @SerialName("createdAt") - val createdAt: String, - ){ + @SerialName("description") + val description: String, + @SerialName("completed") + val completed: Boolean, + ) { fun toModel() = StudyModel.StudyItemModel( id = id, - title = title, - content = content, - thumbnailUrl = thumbnailUrl, - createdAt = createdAt + description = description, + completed = completed ) } diff --git a/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt b/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt index 4511e5e..74863c3 100644 --- a/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt +++ b/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt @@ -5,25 +5,10 @@ import com.kkkk.domain.entity.response.StudyModel import com.kkkk.domain.repository.StudyRepository import javax.inject.Inject -class StudyRepositoryImpl -@Inject -constructor( +class StudyRepositoryImpl @Inject constructor( private val studyDataSource: StudyDataSource, ) : StudyRepository { - override suspend fun getVideos( - page: Int, - size: Int, - ): Result = runCatching { - studyDataSource.getVideos( - page = page, - size = size - ).data.toModel() - } - - override suspend fun getArticles(page: Int, size: Int): Result = runCatching { - studyDataSource.getArticles( - page = page, - size = size - ).data.toModel() + override suspend fun getHomeworks(page: Int, size: Int): Result = runCatching { + studyDataSource.getHomeworks(page, size).data.toModel() } } diff --git a/data/src/main/java/com/kkkk/data/service/StudyService.kt b/data/src/main/java/com/kkkk/data/service/StudyService.kt index cca2bc5..f6645cb 100644 --- a/data/src/main/java/com/kkkk/data/service/StudyService.kt +++ b/data/src/main/java/com/kkkk/data/service/StudyService.kt @@ -6,16 +6,8 @@ import retrofit2.http.GET import retrofit2.http.Query interface StudyService { - @GET("api/v1/videos") - suspend fun getVideos( - @Query("page") - page: Int, - @Query("size") - size: Int - ): BaseResponse - - @GET("api/v1/articles") - suspend fun getArticles( + @GET("api/v1/homeworks") + suspend fun getHomeworks( @Query("page") page: Int, @Query("size") diff --git a/domain/src/main/kotlin/com/kkkk/domain/entity/response/StudyModel.kt b/domain/src/main/kotlin/com/kkkk/domain/entity/response/StudyModel.kt index e389048..61b3651 100644 --- a/domain/src/main/kotlin/com/kkkk/domain/entity/response/StudyModel.kt +++ b/domain/src/main/kotlin/com/kkkk/domain/entity/response/StudyModel.kt @@ -8,9 +8,7 @@ data class StudyModel( ) { data class StudyItemModel( val id: Int = 0, - val title: String = "", - val content: String = "", - val thumbnailUrl: String? = null, - val createdAt: String = "", + val description: String = "", + val completed: Boolean = false, ) } diff --git a/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt b/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt index 6ca1360..c71406a 100644 --- a/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt +++ b/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt @@ -3,12 +3,7 @@ package com.kkkk.domain.repository import com.kkkk.domain.entity.response.StudyModel interface StudyRepository { - suspend fun getVideos( - page: Int, - size: Int - ): Result - - suspend fun getArticles( + suspend fun getHomeworks( page: Int, size: Int ): Result diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyAdapter.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyAdapter.kt new file mode 100644 index 0000000..cbaec41 --- /dev/null +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyAdapter.kt @@ -0,0 +1,35 @@ +package com.kkkk.presentation.main.study + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import com.kkkk.core.util.ItemDiffCallback +import com.kkkk.domain.entity.response.StudyModel +import kr.genti.presentation.databinding.ItemStudyCheckStringBinding + +class StudyAdapter(context: Context, private val isMe: Boolean) : + ListAdapter( + StudyDiffCallback, + ) { + private val inflater by lazy { LayoutInflater.from(context) } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StudyViewHolder { + val binding = ItemStudyCheckStringBinding.inflate(inflater, parent, false) + return StudyViewHolder(binding, isMe = isMe) + } + + override fun onBindViewHolder(holder: StudyViewHolder, position: Int) { + holder.onBind(currentList[position]) + } + + override fun getItemCount() = currentList.size + + companion object { + private val StudyDiffCallback = + ItemDiffCallback( + onItemsTheSame = { old, new -> old.id == new.id }, + onContentsTheSame = { old, new -> old == new }, + ) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index ab23974..bb22b4d 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -20,6 +20,14 @@ import kr.genti.presentation.databinding.FragmentStudyBinding class StudyFragment : BaseFragment(R.layout.fragment_study) { private val viewModel by activityViewModels() + private var _studyStudentAdapter: StudyAdapter? = null + private val studyStudentAdapter + get() = requireNotNull(_studyStudentAdapter) { getString(R.string.error_msg) } + + private var _studyTeacherAdapter: StudyAdapter? = null + private val studyTeacherAdapter + get() = requireNotNull(_studyTeacherAdapter) { getString(R.string.error_msg) } + override fun onViewCreated( view: View, savedInstanceState: Bundle?, @@ -27,17 +35,38 @@ class StudyFragment : BaseFragment(R.layout.fragment_study super.onViewCreated(view, savedInstanceState) setStatusBarColor(R.color.gray_100) + + setAdapter() observeTypeIsMe() + observeStudyList() setToggleClickListener() setAddStudyButtonClickListener() } + private fun setAdapter() { + _studyTeacherAdapter = StudyAdapter(requireContext(), false) + binding.rvTeacherExercise.adapter = studyTeacherAdapter + + _studyStudentAdapter = StudyAdapter(requireContext(), true) + binding.rvStudentExercise.adapter = studyStudentAdapter + } + + private fun observeTypeIsMe() { viewModel.typeIsMe.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { isMe -> setToggleState(isMe) }.launchIn(lifecycleScope) } + private fun observeStudyList() { + viewModel.studyList.flowWithLifecycle(lifecycle).distinctUntilChanged() + .onEach { studyList -> + studyStudentAdapter.submitList(studyList) + studyTeacherAdapter.submitList(studyList) + setToggleState(viewModel.typeIsMe.value) + }.launchIn(lifecycleScope) + } + @SuppressLint("ResourceAsColor") private fun setToggleClickListener() { binding.itemToggle.tvStudent.setOnClickListener { @@ -50,7 +79,6 @@ class StudyFragment : BaseFragment(R.layout.fragment_study private fun setAddStudyButtonClickListener() { binding.btnTeacherExercise.setOnClickListener { - viewModel.addItems(listOf(1, 2, 3)) setToggleState(false) } } @@ -122,4 +150,10 @@ class StudyFragment : BaseFragment(R.layout.fragment_study } } } + + override fun onDestroyView() { + super.onDestroyView() + _studyStudentAdapter = null + _studyTeacherAdapter = null + } } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt new file mode 100644 index 0000000..3ebd02f --- /dev/null +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt @@ -0,0 +1,28 @@ +package com.kkkk.presentation.main.study + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.kkkk.domain.entity.response.StudyModel +import kr.genti.presentation.R +import kr.genti.presentation.databinding.ItemStudyCheckStringBinding + +class StudyViewHolder( + private val binding: ItemStudyCheckStringBinding, + private val isMe: Boolean +) : + RecyclerView.ViewHolder(binding.root) { + fun onBind(data: StudyModel.StudyItemModel) = + with(binding) { + ivCheckbox.setImageResource( + if (data.completed) R.drawable.ic_checkbox_checked else R.drawable.ic_checkbox_unchecked + ) + if (isMe) { + ivDelete.visibility = View.GONE + etString.isEnabled = false + } else{ + ivDelete.visibility = View.VISIBLE + etString.isEnabled = true + } + etString.setText(data.description) + } +} diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt index 4b42da5..995b691 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt @@ -1,12 +1,14 @@ package com.kkkk.presentation.main.study -import android.util.Log import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.kkkk.domain.entity.response.StudyModel import com.kkkk.domain.repository.StudyRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject @HiltViewModel @@ -14,17 +16,25 @@ class StudyViewModel @Inject constructor( private val studyRepository: StudyRepository, ) : ViewModel() { // 추후 과제 리스트 관련으로 사용 - private val _studyList = MutableStateFlow>(emptyList()) - val studyList: StateFlow> = _studyList + private val _studyList = MutableStateFlow>(emptyList()) + val studyList: StateFlow> = _studyList private val _typeIsMe = MutableStateFlow(true) val typeIsMe: StateFlow = _typeIsMe - fun setTypeIsMe(isMe: Boolean) { - _typeIsMe.value = isMe + init { + getHomeworks() + } + + private fun getHomeworks() { + viewModelScope.launch { + studyRepository.getHomeworks(0, 1000).onSuccess { + _studyList.value = it.items + }.onFailure(Timber::e) + } } - fun addItems(items: List) { - _studyList.value += items + fun setTypeIsMe(isMe: Boolean) { + _typeIsMe.value = isMe } } diff --git a/presentation/src/main/res/layout/fragment_study.xml b/presentation/src/main/res/layout/fragment_study.xml index ff7ac94..dafdb4f 100644 --- a/presentation/src/main/res/layout/fragment_study.xml +++ b/presentation/src/main/res/layout/fragment_study.xml @@ -115,15 +115,19 @@ tools:text="진행해야 할 과제에요!" /> + app:layout_constraintTop_toBottomOf="@id/tv_my_exercise" + tools:listitem="@layout/item_study_check_string" /> @@ -172,10 +176,13 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginBottom="12dp" + android:orientation="vertical" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toTopOf="@id/btn_teacher_exercise" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + tools:listitem="@layout/item_study_check_string" /> Date: Sun, 22 Sep 2024 20:47:01 +0900 Subject: [PATCH 11/19] [FEAT/#57] feat click listener --- .../presentation/main/study/StudyAdapter.kt | 4 ++-- .../presentation/main/study/StudyFragment.kt | 16 +++++++++++++--- .../presentation/main/study/StudyViewHolder.kt | 17 +++++++++++++++-- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyAdapter.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyAdapter.kt index cbaec41..d33fd0f 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyAdapter.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyAdapter.kt @@ -8,7 +8,7 @@ import com.kkkk.core.util.ItemDiffCallback import com.kkkk.domain.entity.response.StudyModel import kr.genti.presentation.databinding.ItemStudyCheckStringBinding -class StudyAdapter(context: Context, private val isMe: Boolean) : +class StudyAdapter(context: Context, private val listener: OnItemClickListener, private val isMe: Boolean) : ListAdapter( StudyDiffCallback, ) { @@ -16,7 +16,7 @@ class StudyAdapter(context: Context, private val isMe: Boolean) : override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StudyViewHolder { val binding = ItemStudyCheckStringBinding.inflate(inflater, parent, false) - return StudyViewHolder(binding, isMe = isMe) + return StudyViewHolder(binding, isMe = isMe, listener = listener) } override fun onBindViewHolder(holder: StudyViewHolder, position: Int) { diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index bb22b4d..882f003 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -3,12 +3,14 @@ package com.kkkk.presentation.main.study import android.annotation.SuppressLint import android.os.Bundle import android.view.View +import android.widget.Toast import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.kkkk.core.base.BaseFragment import com.kkkk.core.extension.setStatusBarColor +import com.kkkk.core.extension.toast import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn @@ -17,7 +19,7 @@ import kr.genti.presentation.R import kr.genti.presentation.databinding.FragmentStudyBinding @AndroidEntryPoint -class StudyFragment : BaseFragment(R.layout.fragment_study) { +class StudyFragment : BaseFragment(R.layout.fragment_study), OnItemClickListener { private val viewModel by activityViewModels() private var _studyStudentAdapter: StudyAdapter? = null @@ -44,10 +46,10 @@ class StudyFragment : BaseFragment(R.layout.fragment_study } private fun setAdapter() { - _studyTeacherAdapter = StudyAdapter(requireContext(), false) + _studyTeacherAdapter = StudyAdapter(requireContext(), this, false) binding.rvTeacherExercise.adapter = studyTeacherAdapter - _studyStudentAdapter = StudyAdapter(requireContext(), true) + _studyStudentAdapter = StudyAdapter(requireContext(), this, true) binding.rvStudentExercise.adapter = studyStudentAdapter } @@ -156,4 +158,12 @@ class StudyFragment : BaseFragment(R.layout.fragment_study _studyStudentAdapter = null _studyTeacherAdapter = null } + + override fun onCheckboxClick(itemId: Int, isChecked: Boolean) { + toast("Clicked item ID: $itemId + isChecked: $isChecked") + } + + override fun onDeleteButtonClick(itemId: Int) { + toast("Clicked item ID: $itemId") + } } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt index 3ebd02f..0d501e4 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt @@ -8,7 +8,8 @@ import kr.genti.presentation.databinding.ItemStudyCheckStringBinding class StudyViewHolder( private val binding: ItemStudyCheckStringBinding, - private val isMe: Boolean + private val listener: OnItemClickListener, + private val isMe: Boolean, ) : RecyclerView.ViewHolder(binding.root) { fun onBind(data: StudyModel.StudyItemModel) = @@ -16,13 +17,25 @@ class StudyViewHolder( ivCheckbox.setImageResource( if (data.completed) R.drawable.ic_checkbox_checked else R.drawable.ic_checkbox_unchecked ) + ivCheckbox.setOnClickListener { + listener.onCheckboxClick(data.id, !data.completed) + } if (isMe) { ivDelete.visibility = View.GONE etString.isEnabled = false - } else{ + } else { ivDelete.visibility = View.VISIBLE etString.isEnabled = true + + ivDelete.setOnClickListener { + listener.onDeleteButtonClick(data.id) + } } etString.setText(data.description) } } + +interface OnItemClickListener { + fun onCheckboxClick(itemId: Int, isChecked: Boolean) + fun onDeleteButtonClick(itemId: Int) +} From a84296d7f6fcd8a59c4094bbe9d779776964c560 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 20:53:25 +0900 Subject: [PATCH 12/19] [FEAT/#57] feat item delete --- .../java/com/kkkk/data/dataSource/StudyDataSource.kt | 2 ++ .../com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt | 3 +++ .../com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt | 4 ++++ data/src/main/java/com/kkkk/data/service/StudyService.kt | 9 ++++++++- .../kotlin/com/kkkk/domain/repository/StudyRepository.kt | 4 ++++ .../com/kkkk/presentation/main/study/StudyFragment.kt | 6 +++--- .../com/kkkk/presentation/main/study/StudyViewModel.kt | 9 +++++++++ 7 files changed, 33 insertions(+), 4 deletions(-) diff --git a/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt b/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt index f371ca1..6ff9927 100644 --- a/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt +++ b/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt @@ -5,4 +5,6 @@ import com.kkkk.data.dto.response.StudyDto interface StudyDataSource { suspend fun getHomeworks(page: Int, size: Int): BaseResponse + + suspend fun deleteHomework(homeworkId: Int): BaseResponse } diff --git a/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt b/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt index eac5e53..ab65646 100644 --- a/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt +++ b/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt @@ -11,4 +11,7 @@ data class StudyDataSourceImpl @Inject constructor( ) : StudyDataSource { override suspend fun getHomeworks(page: Int, size: Int): BaseResponse = studyService.getHomeworks(page, size) + + override suspend fun deleteHomework(homeworkId: Int): BaseResponse = + studyService.deleteHomework(homeworkId) } diff --git a/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt b/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt index 74863c3..3e57597 100644 --- a/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt +++ b/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt @@ -11,4 +11,8 @@ class StudyRepositoryImpl @Inject constructor( override suspend fun getHomeworks(page: Int, size: Int): Result = runCatching { studyDataSource.getHomeworks(page, size).data.toModel() } + + override suspend fun deleteHomework(homeworkId: Int): Result = runCatching { + studyDataSource.deleteHomework(homeworkId).data + } } diff --git a/data/src/main/java/com/kkkk/data/service/StudyService.kt b/data/src/main/java/com/kkkk/data/service/StudyService.kt index f6645cb..c2db85d 100644 --- a/data/src/main/java/com/kkkk/data/service/StudyService.kt +++ b/data/src/main/java/com/kkkk/data/service/StudyService.kt @@ -2,7 +2,9 @@ package com.kkkk.data.service import com.kkkk.data.dto.BaseResponse import com.kkkk.data.dto.response.StudyDto +import retrofit2.http.DELETE import retrofit2.http.GET +import retrofit2.http.Path import retrofit2.http.Query interface StudyService { @@ -11,6 +13,11 @@ interface StudyService { @Query("page") page: Int, @Query("size") - size: Int + size: Int, ): BaseResponse + + @DELETE("api/v1/homeworks/{homeworkId}") + suspend fun deleteHomework( + @Path("homeworkId") homeworkId: Int + ): BaseResponse } diff --git a/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt b/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt index c71406a..d4bd4b9 100644 --- a/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt +++ b/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt @@ -7,4 +7,8 @@ interface StudyRepository { page: Int, size: Int ): Result + + suspend fun deleteHomework( + homeworkId: Int + ): Result } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 882f003..9eab0a8 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -3,7 +3,6 @@ package com.kkkk.presentation.main.study import android.annotation.SuppressLint import android.os.Bundle import android.view.View -import android.widget.Toast import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle @@ -19,7 +18,8 @@ import kr.genti.presentation.R import kr.genti.presentation.databinding.FragmentStudyBinding @AndroidEntryPoint -class StudyFragment : BaseFragment(R.layout.fragment_study), OnItemClickListener { +class StudyFragment : BaseFragment(R.layout.fragment_study), + OnItemClickListener { private val viewModel by activityViewModels() private var _studyStudentAdapter: StudyAdapter? = null @@ -164,6 +164,6 @@ class StudyFragment : BaseFragment(R.layout.fragment_study } override fun onDeleteButtonClick(itemId: Int) { - toast("Clicked item ID: $itemId") + viewModel.deleteHomework(itemId) } } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt index 995b691..d22a452 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt @@ -37,4 +37,13 @@ class StudyViewModel @Inject constructor( fun setTypeIsMe(isMe: Boolean) { _typeIsMe.value = isMe } + + fun deleteHomework(homeworkId: Int) { + viewModelScope.launch { + studyRepository.deleteHomework(homeworkId) + .onSuccess { + getHomeworks() + }.onFailure(Timber::e) + } + } } From f437d7d3d484f5e93a3afa6544068dbfdce62b1a Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 21:01:09 +0900 Subject: [PATCH 13/19] [FEAT/#57] feat item update --- .../java/com/kkkk/data/dataSource/StudyDataSource.kt | 6 ++++++ .../kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt | 6 ++++++ .../java/com/kkkk/data/dto/request/HomeworkDto.kt | 12 ++++++++++++ .../kkkk/data/repositoryImpl/StudyRepositoryImpl.kt | 9 +++++++++ .../main/java/com/kkkk/data/service/StudyService.kt | 9 +++++++++ .../com/kkkk/domain/repository/StudyRepository.kt | 10 ++++++++-- .../kkkk/presentation/main/study/StudyFragment.kt | 5 ++--- .../kkkk/presentation/main/study/StudyViewHolder.kt | 4 ++-- .../kkkk/presentation/main/study/StudyViewModel.kt | 9 +++++++++ 9 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 data/src/main/java/com/kkkk/data/dto/request/HomeworkDto.kt diff --git a/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt b/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt index 6ff9927..bf32a0e 100644 --- a/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt +++ b/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt @@ -1,10 +1,16 @@ package com.kkkk.data.dataSource import com.kkkk.data.dto.BaseResponse +import com.kkkk.data.dto.request.HomeworkDto import com.kkkk.data.dto.response.StudyDto interface StudyDataSource { suspend fun getHomeworks(page: Int, size: Int): BaseResponse suspend fun deleteHomework(homeworkId: Int): BaseResponse + + suspend fun updateHomework( + homeworkId: Int, + homeworkDto: HomeworkDto + ): BaseResponse } diff --git a/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt b/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt index ab65646..a2ba384 100644 --- a/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt +++ b/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt @@ -2,6 +2,7 @@ package com.kkkk.data.dataSourceImpl import com.kkkk.data.dataSource.StudyDataSource import com.kkkk.data.dto.BaseResponse +import com.kkkk.data.dto.request.HomeworkDto import com.kkkk.data.dto.response.StudyDto import com.kkkk.data.service.StudyService import javax.inject.Inject @@ -14,4 +15,9 @@ data class StudyDataSourceImpl @Inject constructor( override suspend fun deleteHomework(homeworkId: Int): BaseResponse = studyService.deleteHomework(homeworkId) + + override suspend fun updateHomework( + homeworkId: Int, + homeworkDto: HomeworkDto, + ): BaseResponse = studyService.updateHomework(homeworkId, homeworkDto) } diff --git a/data/src/main/java/com/kkkk/data/dto/request/HomeworkDto.kt b/data/src/main/java/com/kkkk/data/dto/request/HomeworkDto.kt new file mode 100644 index 0000000..15b9f1c --- /dev/null +++ b/data/src/main/java/com/kkkk/data/dto/request/HomeworkDto.kt @@ -0,0 +1,12 @@ +package com.kkkk.data.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class HomeworkDto( + @SerialName("description") + val description: String, + @SerialName("completed") + val completed: Boolean, +) diff --git a/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt b/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt index 3e57597..595fdcf 100644 --- a/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt +++ b/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt @@ -1,6 +1,7 @@ package com.kkkk.data.repositoryImpl import com.kkkk.data.dataSource.StudyDataSource +import com.kkkk.data.dto.request.HomeworkDto import com.kkkk.domain.entity.response.StudyModel import com.kkkk.domain.repository.StudyRepository import javax.inject.Inject @@ -15,4 +16,12 @@ class StudyRepositoryImpl @Inject constructor( override suspend fun deleteHomework(homeworkId: Int): Result = runCatching { studyDataSource.deleteHomework(homeworkId).data } + + override suspend fun updateHomework( + homeworkId: Int, + description: String, + completed: Boolean, + ): Result = runCatching { + studyDataSource.updateHomework(homeworkId, HomeworkDto(description, completed)).data + } } diff --git a/data/src/main/java/com/kkkk/data/service/StudyService.kt b/data/src/main/java/com/kkkk/data/service/StudyService.kt index c2db85d..10c7afd 100644 --- a/data/src/main/java/com/kkkk/data/service/StudyService.kt +++ b/data/src/main/java/com/kkkk/data/service/StudyService.kt @@ -1,9 +1,12 @@ package com.kkkk.data.service import com.kkkk.data.dto.BaseResponse +import com.kkkk.data.dto.request.HomeworkDto import com.kkkk.data.dto.response.StudyDto +import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET +import retrofit2.http.PATCH import retrofit2.http.Path import retrofit2.http.Query @@ -20,4 +23,10 @@ interface StudyService { suspend fun deleteHomework( @Path("homeworkId") homeworkId: Int ): BaseResponse + + @PATCH("api/v1/homeworks/{homeworkId}") + suspend fun updateHomework( + @Path("homeworkId") homeworkId: Int, + @Body homeworkDto: HomeworkDto + ): BaseResponse } diff --git a/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt b/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt index d4bd4b9..faa4683 100644 --- a/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt +++ b/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt @@ -5,10 +5,16 @@ import com.kkkk.domain.entity.response.StudyModel interface StudyRepository { suspend fun getHomeworks( page: Int, - size: Int + size: Int, ): Result suspend fun deleteHomework( - homeworkId: Int + homeworkId: Int, + ): Result + + suspend fun updateHomework( + homeworkId: Int, + description: String, + completed: Boolean, ): Result } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 9eab0a8..5fe7999 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -9,7 +9,6 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.kkkk.core.base.BaseFragment import com.kkkk.core.extension.setStatusBarColor -import com.kkkk.core.extension.toast import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn @@ -159,8 +158,8 @@ class StudyFragment : BaseFragment(R.layout.fragment_study _studyTeacherAdapter = null } - override fun onCheckboxClick(itemId: Int, isChecked: Boolean) { - toast("Clicked item ID: $itemId + isChecked: $isChecked") + override fun onCheckboxClick(itemId: Int, description: String, completed: Boolean) { + viewModel.updateHomework(itemId, description, completed) } override fun onDeleteButtonClick(itemId: Int) { diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt index 0d501e4..b494b45 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt @@ -18,7 +18,7 @@ class StudyViewHolder( if (data.completed) R.drawable.ic_checkbox_checked else R.drawable.ic_checkbox_unchecked ) ivCheckbox.setOnClickListener { - listener.onCheckboxClick(data.id, !data.completed) + listener.onCheckboxClick(data.id, data.description, !data.completed) } if (isMe) { ivDelete.visibility = View.GONE @@ -36,6 +36,6 @@ class StudyViewHolder( } interface OnItemClickListener { - fun onCheckboxClick(itemId: Int, isChecked: Boolean) + fun onCheckboxClick(itemId: Int, description: String, completed: Boolean) fun onDeleteButtonClick(itemId: Int) } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt index d22a452..d2bcc15 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt @@ -46,4 +46,13 @@ class StudyViewModel @Inject constructor( }.onFailure(Timber::e) } } + + fun updateHomework(homeworkId: Int, description: String, completed: Boolean) { + viewModelScope.launch { + studyRepository.updateHomework(homeworkId, description, completed) + .onSuccess { + getHomeworks() + }.onFailure(Timber::e) + } + } } From 54ecea639f6ef22870ae96642386e2a983d96962 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 21:04:34 +0900 Subject: [PATCH 14/19] [FEAT/#57] feat toast observer --- .../com/kkkk/presentation/main/study/StudyFragment.kt | 8 ++++++++ .../com/kkkk/presentation/main/study/StudyViewModel.kt | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 5fe7999..271f38f 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.kkkk.core.base.BaseFragment import com.kkkk.core.extension.setStatusBarColor +import com.kkkk.core.extension.toast import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn @@ -40,6 +41,7 @@ class StudyFragment : BaseFragment(R.layout.fragment_study setAdapter() observeTypeIsMe() observeStudyList() + observeToast() setToggleClickListener() setAddStudyButtonClickListener() } @@ -68,6 +70,12 @@ class StudyFragment : BaseFragment(R.layout.fragment_study }.launchIn(lifecycleScope) } + private fun observeToast() { + viewModel.toast.flowWithLifecycle(lifecycle).onEach { + toast(it) + }.launchIn(lifecycleScope) + } + @SuppressLint("ResourceAsColor") private fun setToggleClickListener() { binding.itemToggle.tvStudent.setOnClickListener { diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt index d2bcc15..f3d4146 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt @@ -5,7 +5,9 @@ import androidx.lifecycle.viewModelScope import com.kkkk.domain.entity.response.StudyModel import com.kkkk.domain.repository.StudyRepository import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import timber.log.Timber @@ -15,13 +17,15 @@ import javax.inject.Inject class StudyViewModel @Inject constructor( private val studyRepository: StudyRepository, ) : ViewModel() { - // 추후 과제 리스트 관련으로 사용 private val _studyList = MutableStateFlow>(emptyList()) val studyList: StateFlow> = _studyList private val _typeIsMe = MutableStateFlow(true) val typeIsMe: StateFlow = _typeIsMe + private val _toast = MutableSharedFlow() + val toast: SharedFlow = _toast + init { getHomeworks() } @@ -42,6 +46,7 @@ class StudyViewModel @Inject constructor( viewModelScope.launch { studyRepository.deleteHomework(homeworkId) .onSuccess { + _toast.emit("삭제되었습니다") getHomeworks() }.onFailure(Timber::e) } @@ -51,6 +56,7 @@ class StudyViewModel @Inject constructor( viewModelScope.launch { studyRepository.updateHomework(homeworkId, description, completed) .onSuccess { + _toast.emit("수정되었습니다") getHomeworks() }.onFailure(Timber::e) } From 124cf14e2f44aec10c0e7a3f26f0bc90783dc41b Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 21:09:11 +0900 Subject: [PATCH 15/19] [FEAT/#57] delete unnecessary api --- .../com/kkkk/presentation/main/study/StudyViewModel.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt index f3d4146..f820d2a 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt @@ -47,7 +47,7 @@ class StudyViewModel @Inject constructor( studyRepository.deleteHomework(homeworkId) .onSuccess { _toast.emit("삭제되었습니다") - getHomeworks() + _studyList.value = _studyList.value.filter { it.id != homeworkId } }.onFailure(Timber::e) } } @@ -57,7 +57,13 @@ class StudyViewModel @Inject constructor( studyRepository.updateHomework(homeworkId, description, completed) .onSuccess { _toast.emit("수정되었습니다") - getHomeworks() + _studyList.value = _studyList.value.map { + if (it.id == homeworkId) { + it.copy(description = description, completed = completed) + } else { + it + } + }.sortedBy { it.completed } }.onFailure(Timber::e) } } From f4d3836b22bdbe9378011a8d6592940f9a4fb06b Mon Sep 17 00:00:00 2001 From: Dongmin Date: Sun, 22 Sep 2024 21:46:33 +0900 Subject: [PATCH 16/19] [FEAT/#57] feat addHomework --- .../kkkk/data/dataSource/StudyDataSource.kt | 4 +- .../dataSourceImpl/StudyDataSourceImpl.kt | 4 + .../dto/request/HomeworkDescriptionDto.kt | 10 +++ .../repositoryImpl/StudyRepositoryImpl.kt | 4 + .../com/kkkk/data/service/StudyService.kt | 7 ++ .../kkkk/domain/repository/StudyRepository.kt | 4 + .../presentation/main/study/StudyFragment.kt | 43 +++++++++- .../presentation/main/study/StudyViewModel.kt | 11 +++ .../src/main/res/drawable/cursor_drawable.xml | 7 ++ .../main/res/layout/dialog_add_homework.xml | 86 +++++++++++++++++++ .../src/main/res/layout/fragment_study.xml | 6 +- 11 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 data/src/main/java/com/kkkk/data/dto/request/HomeworkDescriptionDto.kt create mode 100644 presentation/src/main/res/drawable/cursor_drawable.xml create mode 100644 presentation/src/main/res/layout/dialog_add_homework.xml diff --git a/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt b/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt index bf32a0e..f6c0190 100644 --- a/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt +++ b/data/src/main/java/com/kkkk/data/dataSource/StudyDataSource.kt @@ -7,10 +7,12 @@ import com.kkkk.data.dto.response.StudyDto interface StudyDataSource { suspend fun getHomeworks(page: Int, size: Int): BaseResponse + suspend fun addHomework(description: String): BaseResponse + suspend fun deleteHomework(homeworkId: Int): BaseResponse suspend fun updateHomework( homeworkId: Int, - homeworkDto: HomeworkDto + homeworkDto: HomeworkDto, ): BaseResponse } diff --git a/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt b/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt index a2ba384..85ad87b 100644 --- a/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt +++ b/data/src/main/java/com/kkkk/data/dataSourceImpl/StudyDataSourceImpl.kt @@ -2,6 +2,7 @@ package com.kkkk.data.dataSourceImpl import com.kkkk.data.dataSource.StudyDataSource import com.kkkk.data.dto.BaseResponse +import com.kkkk.data.dto.request.HomeworkDescriptionDto import com.kkkk.data.dto.request.HomeworkDto import com.kkkk.data.dto.response.StudyDto import com.kkkk.data.service.StudyService @@ -13,6 +14,9 @@ data class StudyDataSourceImpl @Inject constructor( override suspend fun getHomeworks(page: Int, size: Int): BaseResponse = studyService.getHomeworks(page, size) + override suspend fun addHomework(description: String): BaseResponse = + studyService.addHomework(HomeworkDescriptionDto(description)) + override suspend fun deleteHomework(homeworkId: Int): BaseResponse = studyService.deleteHomework(homeworkId) diff --git a/data/src/main/java/com/kkkk/data/dto/request/HomeworkDescriptionDto.kt b/data/src/main/java/com/kkkk/data/dto/request/HomeworkDescriptionDto.kt new file mode 100644 index 0000000..adb2e1c --- /dev/null +++ b/data/src/main/java/com/kkkk/data/dto/request/HomeworkDescriptionDto.kt @@ -0,0 +1,10 @@ +package com.kkkk.data.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class HomeworkDescriptionDto( + @SerialName("description") + val description: String, +) diff --git a/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt b/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt index 595fdcf..4bd1cc4 100644 --- a/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt +++ b/data/src/main/java/com/kkkk/data/repositoryImpl/StudyRepositoryImpl.kt @@ -13,6 +13,10 @@ class StudyRepositoryImpl @Inject constructor( studyDataSource.getHomeworks(page, size).data.toModel() } + override suspend fun addHomework(description: String): Result = runCatching { + studyDataSource.addHomework(description).data + } + override suspend fun deleteHomework(homeworkId: Int): Result = runCatching { studyDataSource.deleteHomework(homeworkId).data } diff --git a/data/src/main/java/com/kkkk/data/service/StudyService.kt b/data/src/main/java/com/kkkk/data/service/StudyService.kt index 10c7afd..1fd43a4 100644 --- a/data/src/main/java/com/kkkk/data/service/StudyService.kt +++ b/data/src/main/java/com/kkkk/data/service/StudyService.kt @@ -1,12 +1,14 @@ package com.kkkk.data.service import com.kkkk.data.dto.BaseResponse +import com.kkkk.data.dto.request.HomeworkDescriptionDto import com.kkkk.data.dto.request.HomeworkDto import com.kkkk.data.dto.response.StudyDto import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.PATCH +import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query @@ -19,6 +21,11 @@ interface StudyService { size: Int, ): BaseResponse + @POST("api/v1/homeworks") + suspend fun addHomework( + @Body homeworkDescriptionDto: HomeworkDescriptionDto + ): BaseResponse + @DELETE("api/v1/homeworks/{homeworkId}") suspend fun deleteHomework( @Path("homeworkId") homeworkId: Int diff --git a/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt b/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt index faa4683..968f6d6 100644 --- a/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt +++ b/domain/src/main/kotlin/com/kkkk/domain/repository/StudyRepository.kt @@ -8,6 +8,10 @@ interface StudyRepository { size: Int, ): Result + suspend fun addHomework( + description: String, + ): Result + suspend fun deleteHomework( homeworkId: Int, ): Result diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 271f38f..6f06a27 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -1,8 +1,13 @@ package com.kkkk.presentation.main.study import android.annotation.SuppressLint +import android.app.Dialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.view.View +import android.widget.Button +import android.widget.TextView import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle @@ -16,12 +21,15 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kr.genti.presentation.R import kr.genti.presentation.databinding.FragmentStudyBinding +import java.lang.ref.WeakReference @AndroidEntryPoint class StudyFragment : BaseFragment(R.layout.fragment_study), OnItemClickListener { private val viewModel by activityViewModels() + private var homeworkDialog: WeakReference? = null + private var _studyStudentAdapter: StudyAdapter? = null private val studyStudentAdapter get() = requireNotNull(_studyStudentAdapter) { getString(R.string.error_msg) } @@ -43,7 +51,7 @@ class StudyFragment : BaseFragment(R.layout.fragment_study observeStudyList() observeToast() setToggleClickListener() - setAddStudyButtonClickListener() + setAddHomeworkButtonClickListener() } private fun setAdapter() { @@ -86,9 +94,9 @@ class StudyFragment : BaseFragment(R.layout.fragment_study } } - private fun setAddStudyButtonClickListener() { - binding.btnTeacherExercise.setOnClickListener { - setToggleState(false) + private fun setAddHomeworkButtonClickListener() { + binding.btnTeacherAddHomework.setOnClickListener { + showHomeworkDialog() } } @@ -160,10 +168,37 @@ class StudyFragment : BaseFragment(R.layout.fragment_study } } + private fun showHomeworkDialog() { + val dialog = Dialog(requireContext()).apply { + setContentView(R.layout.dialog_add_homework) + window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + setCanceledOnTouchOutside(false) + setCancelable(true) + + this.findViewById(R.id.btn_study_dialog_cancel).setOnClickListener { + dismiss() + } + + this.findViewById(R.id.btn_study_dialog_save).setOnClickListener { + viewModel.addHomework( + this.findViewById(R.id.et_study_dialog).text.toString() + ) + dismiss() + } + + show() + } + + homeworkDialog = WeakReference(dialog) + } + + override fun onDestroyView() { super.onDestroyView() _studyStudentAdapter = null _studyTeacherAdapter = null + homeworkDialog?.get()?.dismiss() + homeworkDialog = null } override fun onCheckboxClick(itemId: Int, description: String, completed: Boolean) { diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt index f820d2a..da7576e 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewModel.kt @@ -42,6 +42,17 @@ class StudyViewModel @Inject constructor( _typeIsMe.value = isMe } + fun addHomework(description: String) { + viewModelScope.launch { + studyRepository.addHomework(description) + .onSuccess { id -> + _toast.emit("추가되었습니다") + _studyList.value += StudyModel.StudyItemModel(id, description, false) + _studyList.value = _studyList.value.sortedBy { it.completed } + }.onFailure(Timber::e) + } + } + fun deleteHomework(homeworkId: Int) { viewModelScope.launch { studyRepository.deleteHomework(homeworkId) diff --git a/presentation/src/main/res/drawable/cursor_drawable.xml b/presentation/src/main/res/drawable/cursor_drawable.xml new file mode 100644 index 0000000..6e4b358 --- /dev/null +++ b/presentation/src/main/res/drawable/cursor_drawable.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/dialog_add_homework.xml b/presentation/src/main/res/layout/dialog_add_homework.xml new file mode 100644 index 0000000..3bc9b91 --- /dev/null +++ b/presentation/src/main/res/layout/dialog_add_homework.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_study.xml b/presentation/src/main/res/layout/fragment_study.xml index dafdb4f..2698273 100644 --- a/presentation/src/main/res/layout/fragment_study.xml +++ b/presentation/src/main/res/layout/fragment_study.xml @@ -166,7 +166,7 @@ android:layout_height="wrap_content" android:layout_marginBottom="24dp" android:src="@drawable/img_study_empty" - app:layout_constraintBottom_toTopOf="@id/btn_teacher_exercise" + app:layout_constraintBottom_toTopOf="@id/btn_teacher_add_homework" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -178,14 +178,14 @@ android:layout_marginBottom="12dp" android:orientation="vertical" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" - app:layout_constraintBottom_toTopOf="@id/btn_teacher_exercise" + app:layout_constraintBottom_toTopOf="@id/btn_teacher_add_homework" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:listitem="@layout/item_study_check_string" /> Date: Sun, 22 Sep 2024 22:42:56 +0900 Subject: [PATCH 17/19] [FEAT/#57] feat prgressBar --- .../presentation/main/study/StudyFragment.kt | 4 ++++ .../res/drawable/layer_list_progressbar.xml | 18 ++++++++++++++ .../main/res/drawable/shape_seekbar_thumb.xml | 9 +++++++ .../src/main/res/layout/fragment_study.xml | 24 +++++++++++++++---- 4 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 presentation/src/main/res/drawable/layer_list_progressbar.xml create mode 100644 presentation/src/main/res/drawable/shape_seekbar_thumb.xml diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 6f06a27..742d81b 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -75,6 +75,10 @@ class StudyFragment : BaseFragment(R.layout.fragment_study studyStudentAdapter.submitList(studyList) studyTeacherAdapter.submitList(studyList) setToggleState(viewModel.typeIsMe.value) + + binding.progressBarHomework.max = studyList.size + binding.progressBarHomework.progress = studyList.count { it.completed } + binding.ivSeekbarThumb.x = binding.progressBarHomework.width * binding.progressBarHomework.progress / binding.progressBarHomework.max.toFloat() }.launchIn(lifecycleScope) } diff --git a/presentation/src/main/res/drawable/layer_list_progressbar.xml b/presentation/src/main/res/drawable/layer_list_progressbar.xml new file mode 100644 index 0000000..b0970d7 --- /dev/null +++ b/presentation/src/main/res/drawable/layer_list_progressbar.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/drawable/shape_seekbar_thumb.xml b/presentation/src/main/res/drawable/shape_seekbar_thumb.xml new file mode 100644 index 0000000..c7b6329 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_seekbar_thumb.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/presentation/src/main/res/layout/fragment_study.xml b/presentation/src/main/res/layout/fragment_study.xml index 2698273..4642059 100644 --- a/presentation/src/main/res/layout/fragment_study.xml +++ b/presentation/src/main/res/layout/fragment_study.xml @@ -42,7 +42,6 @@ android:layout_height="wrap_content" android:layout_marginTop="24dp" android:background="@drawable/shape_dark_fill_12_rect" - android:paddingHorizontal="16dp" android:paddingVertical="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -54,18 +53,33 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="과제 진척도" + android:paddingHorizontal="16dp" android:textColor="@color/white" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + + Date: Sun, 22 Sep 2024 23:02:33 +0900 Subject: [PATCH 18/19] [FEAT/#57] init empty progress --- .../presentation/main/study/StudyFragment.kt | 16 ++- .../main/study/StudyViewHolder.kt | 2 - .../src/main/res/layout/fragment_study.xml | 100 +++++++++++++----- .../res/layout/item_study_check_string.xml | 5 +- 4 files changed, 87 insertions(+), 36 deletions(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 742d81b..1738167 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -6,7 +6,6 @@ import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.view.View -import android.widget.Button import android.widget.TextView import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels @@ -76,9 +75,18 @@ class StudyFragment : BaseFragment(R.layout.fragment_study studyTeacherAdapter.submitList(studyList) setToggleState(viewModel.typeIsMe.value) - binding.progressBarHomework.max = studyList.size - binding.progressBarHomework.progress = studyList.count { it.completed } - binding.ivSeekbarThumb.x = binding.progressBarHomework.width * binding.progressBarHomework.progress / binding.progressBarHomework.max.toFloat() + if (studyList.isEmpty()) { + binding.layoutHomeworkEmpty.visibility = View.VISIBLE + binding.layoutHomeworkValid.visibility = View.INVISIBLE + } else { + binding.layoutHomeworkEmpty.visibility = View.INVISIBLE + binding.layoutHomeworkValid.visibility = View.VISIBLE + + binding.progressBarHomework.max = studyList.size + binding.progressBarHomework.progress = studyList.count { it.completed } + binding.ivSeekbarThumb.x = + binding.progressBarHomework.width * binding.progressBarHomework.progress / binding.progressBarHomework.max.toFloat() + } }.launchIn(lifecycleScope) } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt index b494b45..4914411 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyViewHolder.kt @@ -22,10 +22,8 @@ class StudyViewHolder( } if (isMe) { ivDelete.visibility = View.GONE - etString.isEnabled = false } else { ivDelete.visibility = View.VISIBLE - etString.isEnabled = true ivDelete.setOnClickListener { listener.onDeleteButtonClick(data.id) diff --git a/presentation/src/main/res/layout/fragment_study.xml b/presentation/src/main/res/layout/fragment_study.xml index 4642059..95f17fc 100644 --- a/presentation/src/main/res/layout/fragment_study.xml +++ b/presentation/src/main/res/layout/fragment_study.xml @@ -47,39 +47,83 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_title"> - + app:layout_constraintTop_toTopOf="parent"> - + + + + + + + app:layout_constraintTop_toTopOf="parent"> - + + + + + + Date: Mon, 23 Sep 2024 00:55:16 +0900 Subject: [PATCH 19/19] [FEAT/#57] apply code review --- .../presentation/main/study/StudyFragment.kt | 120 ++++++++---------- 1 file changed, 51 insertions(+), 69 deletions(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt index 1738167..3bb38fd 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/study/StudyFragment.kt @@ -8,6 +8,7 @@ import android.os.Bundle import android.view.View import android.widget.TextView import androidx.core.content.ContextCompat +import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope @@ -75,17 +76,19 @@ class StudyFragment : BaseFragment(R.layout.fragment_study studyTeacherAdapter.submitList(studyList) setToggleState(viewModel.typeIsMe.value) - if (studyList.isEmpty()) { - binding.layoutHomeworkEmpty.visibility = View.VISIBLE - binding.layoutHomeworkValid.visibility = View.INVISIBLE - } else { - binding.layoutHomeworkEmpty.visibility = View.INVISIBLE - binding.layoutHomeworkValid.visibility = View.VISIBLE - - binding.progressBarHomework.max = studyList.size - binding.progressBarHomework.progress = studyList.count { it.completed } - binding.ivSeekbarThumb.x = - binding.progressBarHomework.width * binding.progressBarHomework.progress / binding.progressBarHomework.max.toFloat() + with(binding) { + if (studyList.isEmpty()) { + layoutHomeworkEmpty.visibility = View.VISIBLE + layoutHomeworkValid.visibility = View.INVISIBLE + } else { + layoutHomeworkEmpty.visibility = View.INVISIBLE + layoutHomeworkValid.visibility = View.VISIBLE + + progressBarHomework.max = studyList.size + progressBarHomework.progress = studyList.count { it.completed } + ivSeekbarThumb.x = + progressBarHomework.width * progressBarHomework.progress / progressBarHomework.max.toFloat() + } } }.launchIn(lifecycleScope) } @@ -113,70 +116,49 @@ class StudyFragment : BaseFragment(R.layout.fragment_study } private fun setToggleState(isMe: Boolean) { - when (isMe) { - true -> { - with(binding) { - with(itemToggle) { - tvStudent.setBackgroundResource(R.drawable.shape_toggle_selected) - tvStudent.setTextColor( - ContextCompat.getColor( - requireContext(), - R.color.black - ) - ) - tvTeacher.background = null - tvTeacher.setTextColor( - ContextCompat.getColor( - requireContext(), - R.color.white - ) - ) - } - - layoutMyExercise.visibility = View.VISIBLE - layoutTeacherExercise.visibility = View.GONE + setToggleAppearance(isMe) + setLayoutVisibility(isMe) + updateExerciseVisibility(isMe) + } - if (viewModel.studyList.value.isEmpty()) { - ivMyExerciseEmpty.visibility = View.VISIBLE - layoutMyExerciseValid.visibility = View.GONE - } else { - ivMyExerciseEmpty.visibility = View.GONE - layoutMyExerciseValid.visibility = View.VISIBLE - } - } + private fun setToggleAppearance(isMe: Boolean) { + with(binding.itemToggle) { + val (selectedView, unselectedView) = if (isMe) { + tvStudent to tvTeacher + } else { + tvTeacher to tvStudent } - false -> { - with(binding) { - with(itemToggle) { - tvTeacher.setBackgroundResource(R.drawable.shape_toggle_selected) - tvTeacher.setTextColor( - ContextCompat.getColor( - requireContext(), - R.color.black - ) - ) - tvStudent.background = null - tvStudent.setTextColor( - ContextCompat.getColor( - requireContext(), - R.color.white - ) - ) - } + selectedView.apply { + setBackgroundResource(R.drawable.shape_toggle_selected) + setTextColor(ContextCompat.getColor(requireContext(), R.color.black)) + } + unselectedView.apply { + background = null + setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) + } + } + } - layoutMyExercise.visibility = View.GONE - layoutTeacherExercise.visibility = View.VISIBLE + private fun setLayoutVisibility(isMe: Boolean) { + with(binding) { + layoutMyExercise.isVisible = isMe + layoutTeacherExercise.isVisible = !isMe + } + } - if (viewModel.studyList.value.isEmpty()) { - ivTeacherExerciseEmpty.visibility = View.VISIBLE - rvTeacherExercise.visibility = View.GONE - } else { - ivTeacherExerciseEmpty.visibility = View.GONE - rvTeacherExercise.visibility = View.VISIBLE - } - } + private fun updateExerciseVisibility(isMe: Boolean) { + with(binding) { + val (emptyView, exerciseView) = if (isMe) { + ivMyExerciseEmpty to layoutMyExerciseValid + } else { + ivTeacherExerciseEmpty to rvTeacherExercise } + + val isEmpty = viewModel.studyList.value.isEmpty() + + emptyView.isVisible = isEmpty + exerciseView.isVisible = !isEmpty } }