Skip to content

Commit be8bc85

Browse files
committed
✨ 그룹 수정하기 기능 추가
1 parent 4bece6d commit be8bc85

File tree

7 files changed

+337
-3
lines changed

7 files changed

+337
-3
lines changed

presentation/src/main/java/com/whyranoid/presentation/community/group/detail/GroupDetailFragment.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.os.Bundle
44
import android.view.View
55
import android.widget.Toast
66
import androidx.fragment.app.viewModels
7+
import androidx.navigation.fragment.findNavController
78
import androidx.navigation.fragment.navArgs
89
import com.google.android.material.snackbar.Snackbar
910
import com.whyranoid.presentation.R
@@ -42,7 +43,8 @@ internal class GroupDetailFragment :
4243
val dialog = GroupSettingDialog(
4344
// TODO : 그룹 수정으로 이동
4445
onEditButtonClickListener = {
45-
Toast.makeText(context, "그룹 수정하기", Toast.LENGTH_SHORT).show()
46+
val action = GroupDetailFragmentDirections.actionGroupDetailFragmentToEditGroupFragment(groupDetailArgs.groupInfo)
47+
findNavController().navigate(action)
4648
},
4749
// TODO : 그룹 삭제
4850
onDeleteButtonClickListener = {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.whyranoid.presentation.community.group.edit
2+
3+
import android.os.Bundle
4+
import android.view.View
5+
import androidx.fragment.app.viewModels
6+
import androidx.navigation.fragment.findNavController
7+
import com.google.android.material.snackbar.Snackbar
8+
import com.whyranoid.presentation.R
9+
import com.whyranoid.presentation.base.BaseFragment
10+
import com.whyranoid.presentation.databinding.FragmentEditGroupBinding
11+
import com.whyranoid.presentation.util.repeatWhenUiStarted
12+
import dagger.hilt.android.AndroidEntryPoint
13+
14+
@AndroidEntryPoint
15+
internal class EditGroupFragment :
16+
BaseFragment<FragmentEditGroupBinding>(R.layout.fragment_edit_group) {
17+
18+
private val viewModel: EditGroupViewModel by viewModels()
19+
20+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
21+
super.onViewCreated(view, savedInstanceState)
22+
23+
setupMenu()
24+
initViews()
25+
observeState()
26+
}
27+
28+
private fun initViews() {
29+
binding.viewModel = viewModel
30+
}
31+
32+
private fun handleEvent(event: Event) {
33+
when (event) {
34+
// TODO : 다이어로그로 그룹을 수정할 수 있도록 변경
35+
is Event.AddRuleButtonClick -> {
36+
Snackbar.make(binding.root, "룰 추가 클릭", Snackbar.LENGTH_SHORT).show()
37+
}
38+
is Event.EditGroupButtonClick -> {
39+
if (event.isSuccess) {
40+
Snackbar.make(
41+
binding.root,
42+
getString(R.string.text_edit_group_success),
43+
Snackbar.LENGTH_SHORT
44+
).show()
45+
findNavController().popBackStack()
46+
} else {
47+
Snackbar.make(
48+
binding.root,
49+
getString(R.string.text_edit_group_fail),
50+
Snackbar.LENGTH_SHORT
51+
).show()
52+
}
53+
}
54+
is Event.WarningButtonClick -> {
55+
Snackbar.make(
56+
binding.root,
57+
getString(R.string.text_warning_create_group),
58+
Snackbar.LENGTH_SHORT
59+
).show()
60+
}
61+
}
62+
}
63+
64+
private fun observeState() {
65+
viewLifecycleOwner.repeatWhenUiStarted {
66+
viewModel.eventFlow.collect { event ->
67+
handleEvent(event)
68+
}
69+
}
70+
71+
viewLifecycleOwner.repeatWhenUiStarted {
72+
viewModel.isButtonEnable.collect { isEnable ->
73+
if (isEnable) {
74+
binding.topAppBar.menu.setGroupVisible(R.id.ready_to_create, true)
75+
binding.topAppBar.menu.setGroupVisible(R.id.not_ready_to_create, false)
76+
} else {
77+
binding.topAppBar.menu.setGroupVisible(R.id.ready_to_create, false)
78+
binding.topAppBar.menu.setGroupVisible(R.id.not_ready_to_create, true)
79+
}
80+
}
81+
}
82+
}
83+
84+
private fun setupMenu() {
85+
with(binding.topAppBar) {
86+
inflateMenu(R.menu.create_group_menu)
87+
88+
setOnMenuItemClickListener { menuItem ->
89+
when (menuItem.itemId) {
90+
R.id.create_group_button -> {
91+
viewModel.emitEvent(Event.EditGroupButtonClick())
92+
true
93+
}
94+
R.id.warning_about_create_group_button -> {
95+
viewModel.emitEvent(Event.WarningButtonClick)
96+
true
97+
}
98+
else -> {
99+
false
100+
}
101+
}
102+
}
103+
}
104+
}
105+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.whyranoid.presentation.community.group.edit
2+
3+
import androidx.lifecycle.SavedStateHandle
4+
import androidx.lifecycle.ViewModel
5+
import androidx.lifecycle.viewModelScope
6+
import com.whyranoid.domain.model.toRule
7+
import com.whyranoid.domain.usecase.UpdateGroupInfoUseCase
8+
import com.whyranoid.presentation.model.GroupInfoUiModel
9+
import dagger.hilt.android.lifecycle.HiltViewModel
10+
import kotlinx.coroutines.flow.MutableSharedFlow
11+
import kotlinx.coroutines.flow.MutableStateFlow
12+
import kotlinx.coroutines.flow.SharingStarted
13+
import kotlinx.coroutines.flow.StateFlow
14+
import kotlinx.coroutines.flow.asSharedFlow
15+
import kotlinx.coroutines.flow.combine
16+
import kotlinx.coroutines.flow.stateIn
17+
import kotlinx.coroutines.launch
18+
import javax.inject.Inject
19+
20+
@HiltViewModel
21+
class EditGroupViewModel @Inject constructor(
22+
private val updateGroupInfoUseCase: UpdateGroupInfoUseCase,
23+
stateHandle: SavedStateHandle
24+
) : ViewModel() {
25+
26+
private val initGroupInfo = stateHandle.get<GroupInfoUiModel>("groupInfo")!!
27+
28+
val groupName = MutableStateFlow<String>(initGroupInfo.name)
29+
val groupIntroduce = MutableStateFlow<String>(initGroupInfo.introduce)
30+
val rules = MutableStateFlow<List<String>>(initGroupInfo.rules.map { it.toString() })
31+
32+
private val _eventFlow = MutableSharedFlow<Event>()
33+
val eventFlow = _eventFlow.asSharedFlow()
34+
35+
val isButtonEnable: StateFlow<Boolean>
36+
get() = groupName.combine(groupIntroduce) { name, introduce ->
37+
name.trim().isNotEmpty() && introduce.trim().isNotEmpty()
38+
}.stateIn(
39+
scope = viewModelScope,
40+
initialValue = false,
41+
started = SharingStarted.WhileSubscribed(5000)
42+
)
43+
44+
// TODO : 중복확인 로직
45+
fun onDuplicateCheckButtonClicked() {
46+
}
47+
48+
fun onAddRuleButtonClicked() {
49+
emitEvent(Event.AddRuleButtonClick)
50+
}
51+
52+
fun emitEvent(event: Event) {
53+
when (event) {
54+
is Event.AddRuleButtonClick,
55+
Event.WarningButtonClick -> {
56+
viewModelScope.launch {
57+
_eventFlow.emit(event)
58+
}
59+
}
60+
// TODO : 성공 여부에 따른 분기처리
61+
is Event.EditGroupButtonClick -> {
62+
viewModelScope.launch {
63+
val isSuccess = updateGroupInfoUseCase(
64+
groupId = initGroupInfo.groupId,
65+
groupName = groupName.value,
66+
groupIntroduce = groupIntroduce.value,
67+
rules = rules.value.map {
68+
it.toRule()
69+
}
70+
)
71+
if (isSuccess) {
72+
_eventFlow.emit(event)
73+
} else {
74+
_eventFlow.emit(Event.EditGroupButtonClick(false))
75+
}
76+
}
77+
}
78+
}
79+
}
80+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.whyranoid.presentation.community.group.edit
2+
3+
// TODO : create와 거의 동일함..
4+
sealed class Event {
5+
data class EditGroupButtonClick(val isSuccess: Boolean = true) : Event()
6+
object WarningButtonClick : Event()
7+
object AddRuleButtonClick : Event()
8+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<layout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools">
5+
6+
<data>
7+
8+
<variable
9+
name="viewModel"
10+
type="com.whyranoid.presentation.community.group.edit.EditGroupViewModel" />
11+
</data>
12+
13+
<androidx.constraintlayout.widget.ConstraintLayout
14+
android:layout_width="match_parent"
15+
android:layout_height="match_parent"
16+
tools:context=".community.group.create.CreateGroupFragment">
17+
18+
<com.google.android.material.appbar.AppBarLayout
19+
android:id="@+id/app_bar"
20+
android:layout_width="match_parent"
21+
android:layout_height="wrap_content"
22+
app:layout_constraintTop_toTopOf="parent">
23+
24+
<com.google.android.material.appbar.MaterialToolbar
25+
android:id="@+id/top_app_bar"
26+
style="@style/Widget.MaterialComponents.Toolbar.Primary"
27+
android:layout_width="match_parent"
28+
android:layout_height="?attr/actionBarSize"
29+
app:title="@string/text_edit_group" />
30+
31+
</com.google.android.material.appbar.AppBarLayout>
32+
33+
34+
<com.google.android.material.textfield.TextInputLayout
35+
android:id="@+id/text_input_layout_group_name"
36+
android:layout_width="0dp"
37+
android:layout_height="wrap_content"
38+
android:layout_margin="16dp"
39+
android:background="@color/mogakrun_primary"
40+
app:counterEnabled="true"
41+
app:layout_constraintEnd_toStartOf="@id/btn_duplicate_check"
42+
app:layout_constraintStart_toStartOf="parent"
43+
app:layout_constraintTop_toBottomOf="@id/app_bar">
44+
45+
<com.google.android.material.textfield.TextInputEditText
46+
android:id="@+id/et_group_name"
47+
android:layout_width="match_parent"
48+
android:layout_height="wrap_content"
49+
android:background="@color/mogakrun_secondary"
50+
android:hint="@string/text_group_name"
51+
android:maxLength="20"
52+
android:text="@={viewModel.groupName}"
53+
android:textColor="@color/mogakrun_on_secondary" />
54+
55+
</com.google.android.material.textfield.TextInputLayout>
56+
57+
<com.google.android.material.button.MaterialButton
58+
android:id="@+id/btn_duplicate_check"
59+
android:layout_width="wrap_content"
60+
android:layout_height="wrap_content"
61+
android:layout_margin="16dp"
62+
android:enabled="@{viewModel.isButtonEnable()}"
63+
android:onClick="@{() -> viewModel.onDuplicateCheckButtonClicked()}"
64+
android:text="@string/text_duplicate_check"
65+
app:layout_constraintBottom_toTopOf="@id/time_and_date_picker"
66+
app:layout_constraintEnd_toEndOf="parent"
67+
app:layout_constraintTop_toBottomOf="@id/app_bar" />
68+
69+
<TextView
70+
android:id="@+id/time_and_date_picker"
71+
style="@style/MoGakRunText.Bold.Medium"
72+
android:layout_width="0dp"
73+
android:layout_height="70dp"
74+
android:layout_margin="16dp"
75+
android:background="@color/mogakrun_secondary_dark"
76+
android:gravity="center_vertical"
77+
android:text="@{viewModel.rules.toString()}"
78+
app:layout_constraintEnd_toStartOf="@id/btn_select_rule"
79+
app:layout_constraintStart_toStartOf="parent"
80+
app:layout_constraintTop_toBottomOf="@id/text_input_layout_group_name"
81+
tools:text="규칙들 들어갈 자리입니다~" />
82+
83+
<com.google.android.material.button.MaterialButton
84+
android:id="@+id/btn_select_rule"
85+
android:layout_width="wrap_content"
86+
android:layout_height="wrap_content"
87+
android:layout_margin="16dp"
88+
android:onClick="@{() -> viewModel.onAddRuleButtonClicked()}"
89+
android:text="@string/text_select_rule"
90+
app:layout_constraintBottom_toTopOf="@id/text_input_layout_group_introduce"
91+
app:layout_constraintEnd_toEndOf="parent"
92+
app:layout_constraintTop_toBottomOf="@id/text_input_layout_group_name" />
93+
94+
<com.google.android.material.textfield.TextInputLayout
95+
android:id="@+id/text_input_layout_group_introduce"
96+
android:layout_width="0dp"
97+
android:layout_height="0dp"
98+
android:layout_margin="16dp"
99+
android:background="@color/mogakrun_primary"
100+
android:gravity="top|start"
101+
app:counterEnabled="true"
102+
app:layout_constraintBottom_toBottomOf="parent"
103+
app:layout_constraintEnd_toEndOf="parent"
104+
app:layout_constraintStart_toStartOf="parent"
105+
app:layout_constraintTop_toBottomOf="@id/time_and_date_picker">
106+
107+
<com.google.android.material.textfield.TextInputEditText
108+
android:id="@+id/text_group_introduce"
109+
android:layout_width="match_parent"
110+
android:layout_height="match_parent"
111+
android:layout_weight="1"
112+
android:background="@color/mogakrun_secondary"
113+
android:gravity="top|start"
114+
android:hint="@string/text_group_introduce"
115+
android:maxLength="200"
116+
android:minLines="2"
117+
android:text="@={viewModel.groupIntroduce}"
118+
android:textColor="@color/mogakrun_on_secondary" />
119+
120+
</com.google.android.material.textfield.TextInputLayout>
121+
122+
</androidx.constraintlayout.widget.ConstraintLayout>
123+
</layout>

presentation/src/main/res/navigation/navigation_graph.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
</fragment>
3939
<fragment
4040
android:id="@+id/createGroupFragment"
41-
android:name="com.whyranoid.presentation.community.group.CreateGroupFragment"
41+
android:name="com.whyranoid.presentation.community.group.create.CreateGroupFragment"
4242
android:label="CreateGroupFragment"
4343
tools:layout="@layout/fragment_create_group"/>
4444
<fragment
@@ -49,6 +49,18 @@
4949
<argument
5050
android:name="groupInfo"
5151
app:argType="com.whyranoid.presentation.model.GroupInfoUiModel" />
52+
<action
53+
android:id="@+id/action_groupDetailFragment_to_editGroupFragment"
54+
app:destination="@id/editGroupFragment" />
55+
</fragment>
56+
<fragment
57+
android:id="@+id/editGroupFragment"
58+
android:name="com.whyranoid.presentation.community.group.edit.EditGroupFragment"
59+
android:label="EditGroupFragment"
60+
tools:layout="@layout/fragment_edit_group">
61+
<argument
62+
android:name="groupInfo"
63+
app:argType="com.whyranoid.presentation.model.GroupInfoUiModel" />
5264
</fragment>
5365

5466
</navigation>

presentation/src/main/res/values/strings.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
<string name="running_history_label_finish_running_time">종료시간</string>
5858
<string name="running_history_label_running_distance">이동거리</string>
5959
<string name="running_history_label_running_pace">페이스</string>
60-
60+
6161
<!-- 그룹 상세보기 화면-->
6262
<string name="text_headcount">%d명</string>
6363
<string name="text_recruit">홍보하기</string>
@@ -67,4 +67,8 @@
6767
<string name="text_delete_group">그룹 삭제하기</string>
6868
<string name="text_cancel">취소</string>
6969

70+
<!-- 그룹 수정하기 화면-->
71+
<string name="text_edit_group_success">그룹이 수정되었습니다!</string>
72+
<string name="text_edit_group_fail">그룹 수정이 실패하였습니다.</string>
73+
7074
</resources>

0 commit comments

Comments
 (0)