Skip to content

Commit 583b2a7

Browse files
authored
πŸ”€ #65 from boostcampwm-2022/feat/community_create_running_post
인증글 μ™„λ£Œ μ‹œ UX 및 ν™”λ©΄ 이동 κ΅¬ν˜„, 달λ ₯에 μš΄λ™ν•œ λ‚ μ§œ ν‘œμ‹œν•˜κΈ°, UI μˆ˜μ •
2 parents 786fa54 + 765c786 commit 583b2a7

30 files changed

+309
-113
lines changed

β€Žpresentation/src/main/java/com/whyranoid/presentation/MainActivity.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.whyranoid.presentation
22

33
import android.os.Bundle
4+
import android.view.View
45
import androidx.appcompat.app.AppCompatActivity
56
import androidx.navigation.fragment.NavHostFragment
67
import androidx.navigation.ui.NavigationUI
@@ -22,6 +23,15 @@ class MainActivity : AppCompatActivity() {
2223
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
2324
val navController = navHostFragment.navController
2425

26+
navController.addOnDestinationChangedListener { _, destination, _ ->
27+
when (destination.id) {
28+
R.id.communityFragment, R.id.runningStartFragment, R.id.myRunFragment ->
29+
binding.bottomNavigation.visibility = View.VISIBLE
30+
31+
else -> binding.bottomNavigation.visibility = View.GONE
32+
}
33+
}
34+
2535
NavigationUI.setupWithNavController(binding.bottomNavigation, navController)
2636
}
2737
}

β€Žpresentation/src/main/java/com/whyranoid/presentation/community/runningpost/CreateRunningPostFragment.kt

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package com.whyranoid.presentation.community.runningpost
33
import android.os.Bundle
44
import android.view.View
55
import androidx.fragment.app.viewModels
6+
import androidx.navigation.fragment.findNavController
67
import com.google.android.material.snackbar.Snackbar
78
import com.whyranoid.presentation.R
89
import com.whyranoid.presentation.base.BaseFragment
910
import com.whyranoid.presentation.databinding.FragmentCreateRunningPostBinding
11+
import com.whyranoid.presentation.model.UiState
1012
import com.whyranoid.presentation.util.repeatWhenUiStarted
1113
import dagger.hilt.android.AndroidEntryPoint
1214

@@ -20,6 +22,7 @@ internal class CreateRunningPostFragment :
2022
super.onViewCreated(view, savedInstanceState)
2123

2224
initViews()
25+
observeState()
2326
}
2427

2528
private fun initViews() {
@@ -28,6 +31,19 @@ internal class CreateRunningPostFragment :
2831
setUpMenu()
2932
}
3033

34+
private fun observeState() {
35+
viewLifecycleOwner.repeatWhenUiStarted {
36+
viewModel.createPostState.collect { createPostState ->
37+
when (createPostState) {
38+
is UiState.UnInitialized -> {}
39+
is UiState.Loading -> {}
40+
is UiState.Success<Boolean> -> handleCreatePostStateSuccess(createPostState.value)
41+
is UiState.Failure -> {}
42+
}
43+
}
44+
}
45+
}
46+
3147
private fun setUpMenu() {
3248
with(binding.topAppBar) {
3349
inflateMenu(R.menu.community_create_running_post_menu)
@@ -51,7 +67,11 @@ internal class CreateRunningPostFragment :
5167
true
5268
}
5369
R.id.warning_about_create_running_post_button -> {
54-
Snackbar.make(binding.root, getString(R.string.community_warning_running_post), Snackbar.LENGTH_SHORT)
70+
Snackbar.make(
71+
binding.root,
72+
getString(R.string.community_warning_running_post),
73+
Snackbar.LENGTH_SHORT
74+
)
5575
.show()
5676
true
5777
}
@@ -62,4 +82,27 @@ internal class CreateRunningPostFragment :
6282
}
6383
}
6484
}
85+
86+
private fun handleCreatePostStateSuccess(isSuccess: Boolean) {
87+
if (isSuccess) {
88+
val action =
89+
CreateRunningPostFragmentDirections.actionCreateRunningPostFragmentToCommunityFragment()
90+
91+
Snackbar.make(
92+
requireView(),
93+
getString(R.string.community_success_create_running_post),
94+
Snackbar.LENGTH_SHORT
95+
)
96+
.show()
97+
98+
findNavController().navigate(action)
99+
} else {
100+
Snackbar.make(
101+
requireView(),
102+
getString(R.string.community_fail_create_running_post),
103+
Snackbar.LENGTH_SHORT
104+
)
105+
.show()
106+
}
107+
}
65108
}

β€Žpresentation/src/main/java/com/whyranoid/presentation/community/runningpost/CreateRunningPostViewModel.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import androidx.lifecycle.ViewModel
55
import androidx.lifecycle.viewModelScope
66
import com.whyranoid.domain.usecase.CreateRunningPostUseCase
77
import com.whyranoid.presentation.model.RunningHistoryUiModel
8+
import com.whyranoid.presentation.model.UiState
89
import dagger.hilt.android.lifecycle.HiltViewModel
910
import kotlinx.coroutines.flow.MutableStateFlow
1011
import kotlinx.coroutines.flow.SharingStarted
1112
import kotlinx.coroutines.flow.StateFlow
13+
import kotlinx.coroutines.flow.asStateFlow
1214
import kotlinx.coroutines.flow.map
1315
import kotlinx.coroutines.flow.stateIn
1416
import kotlinx.coroutines.launch
@@ -33,13 +35,23 @@ class CreateRunningPostViewModel @Inject constructor(
3335
scope = viewModelScope
3436
)
3537

38+
private val _createPostState = MutableStateFlow<UiState<Boolean>>(UiState.UnInitialized)
39+
val createPostState: StateFlow<UiState<Boolean>>
40+
get() = _createPostState.asStateFlow()
41+
3642
fun createRunningPost() {
3743
viewModelScope.launch {
44+
_createPostState.value = UiState.Loading
45+
3846
selectedRunningHistory?.let { runningHistory ->
3947
createRunningPostUseCase(
4048
runningPostContent.value.toString(),
4149
runningHistory.historyId
42-
)
50+
).onSuccess { createPostResult ->
51+
_createPostState.value = UiState.Success(createPostResult)
52+
}.onFailure { throwable ->
53+
_createPostState.value = UiState.Failure(throwable)
54+
}
4355
}
4456
}
4557
}

β€Žpresentation/src/main/java/com/whyranoid/presentation/myrun/CalendarDayBinder.kt

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import com.whyranoid.presentation.databinding.ItemCalendarDayBinding
1212
import java.time.LocalDate
1313

1414
class CalendarDayBinder(
15-
private val calendarView: CalendarView
15+
private val calendarView: CalendarView,
16+
private val runningDays: List<List<String>>
1617
) : DayBinder<CalendarDayBinder.DayContainer> {
1718
private var calendar: CalendarRange = CalendarRange(null, null)
1819

@@ -36,12 +37,6 @@ class CalendarDayBinder(
3637
)
3738
)
3839
// day.day와 day.date.monthValueλ₯Ό μ§€μ •ν•΄μ„œ νŠΉμ • μ›”, 일에 λ‹¬λ Έλ‹€λŠ” 콩 ν‘œμ‹œ κ°€λŠ₯
39-
} else if (day.day == 10 && day.date.monthValue == 11) {
40-
container.binding.root.background =
41-
ContextCompat.getDrawable(
42-
calendarView.context,
43-
R.drawable.calendar_kong
44-
)
4540
} else {
4641
container.binding.tvCalendarDay.setTextColor(
4742
ContextCompat.getColor(
@@ -74,6 +69,18 @@ class CalendarDayBinder(
7469
R.drawable.thumbnail_src_small
7570
)
7671
}
72+
73+
runningDays.forEach { runningDay ->
74+
val (runningDayYear, runningDayMonth, runningDayDay) = runningDay.map { it.toInt() }
75+
76+
if (runningDayYear == day.date.year && runningDayMonth == day.date.monthValue && runningDayDay == day.day) {
77+
container.binding.root.background =
78+
ContextCompat.getDrawable(
79+
calendarView.context,
80+
R.drawable.calendar_kong
81+
)
82+
}
83+
}
7784
}
7885

7986
private fun isInRange(date: LocalDate): Boolean {

β€Žpresentation/src/main/java/com/whyranoid/presentation/myrun/MyRunFragment.kt

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,6 @@ internal class MyRunFragment : BaseFragment<FragmentMyRunBinding>(R.layout.fragm
5757
}
5858
true
5959
}
60-
61-
calendarView.apply {
62-
itemAnimator = null
63-
dayBinder = CalendarDayBinder(this)
64-
monthScrollListener = { calendarMonth ->
65-
onMonthScrolled(calendarMonth.yearMonth)
66-
}
67-
// λͺ¨λ“  달λ ₯ λ²”μœ„ μ„€μ •
68-
setup(firstMonth, lastMonth, firstDayOfWeek)
69-
// 첫 ν™”λ©΄μ—μ„œ 보일 달 μ„€μ •
70-
scrollToMonth(currentMonth)
71-
}
7260
}
7361

7462
private fun observeState() {
@@ -100,6 +88,22 @@ internal class MyRunFragment : BaseFragment<FragmentMyRunBinding>(R.layout.fragm
10088
}
10189
}
10290
}
91+
92+
viewLifecycleOwner.repeatWhenUiStarted {
93+
viewModel.runningDays.collect { runningDays ->
94+
binding.calendarView.apply {
95+
itemAnimator = null
96+
dayBinder = CalendarDayBinder(this, runningDays)
97+
monthScrollListener = { calendarMonth ->
98+
onMonthScrolled(calendarMonth.yearMonth)
99+
}
100+
// λͺ¨λ“  달λ ₯ λ²”μœ„ μ„€μ •
101+
setup(firstMonth, lastMonth, firstDayOfWeek)
102+
// 첫 ν™”λ©΄μ—μ„œ 보일 달 μ„€μ •
103+
scrollToMonth(currentMonth)
104+
}
105+
}
106+
}
103107
}
104108

105109
private fun handleRunningHistorySuccessState(runningHistoryList: List<RunningHistoryUiModel>) {

β€Žpresentation/src/main/java/com/whyranoid/presentation/myrun/MyRunViewModel.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.whyranoid.presentation.myrun
22

33
import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
5+
import com.whyranoid.domain.model.RunningHistory
56
import com.whyranoid.domain.usecase.GetNicknameUseCase
67
import com.whyranoid.domain.usecase.GetProfileUriUseCase
78
import com.whyranoid.domain.usecase.GetRunningHistoryUseCase
@@ -10,6 +11,7 @@ import com.whyranoid.domain.usecase.UpdateNicknameUseCase
1011
import com.whyranoid.presentation.model.RunningHistoryUiModel
1112
import com.whyranoid.presentation.model.UiState
1213
import com.whyranoid.presentation.model.toRunningHistoryUiModel
14+
import com.whyranoid.presentation.util.toRunningDateString
1315
import dagger.hilt.android.lifecycle.HiltViewModel
1416
import kotlinx.coroutines.flow.MutableStateFlow
1517
import kotlinx.coroutines.flow.StateFlow
@@ -50,6 +52,11 @@ class MyRunViewModel @Inject constructor(
5052
val runningHistoryListState: StateFlow<UiState<List<RunningHistoryUiModel>>>
5153
get() = _runningHistoryListState.asStateFlow()
5254

55+
private val _runningDays =
56+
MutableStateFlow<List<List<String>>>(emptyList())
57+
val runningDays: StateFlow<List<List<String>>>
58+
get() = _runningDays.asStateFlow()
59+
5360
private fun getUid() {
5461
viewModelScope.launch {
5562
_uid.value = getUidUseCase()
@@ -90,14 +97,24 @@ class MyRunViewModel @Inject constructor(
9097
runningHistoryListResult.onSuccess { runningHistoryList ->
9198
_runningHistoryListState.value =
9299
UiState.Success(runningHistoryList.map { runningHistory -> runningHistory.toRunningHistoryUiModel() })
100+
getRunningDays(runningHistoryList)
93101
}.onFailure { throwable ->
94102
_runningHistoryListState.value = UiState.Failure(throwable)
95103
}
96104
}
97105
}
98106
}
99107

108+
private fun getRunningDays(runningHistoryList: List<RunningHistory>) {
109+
runningHistoryList.map { runningHistory ->
110+
_runningDays.value += listOf(
111+
runningHistory.startedAt.toRunningDateString().split(RUNNING_DATE_SEPARATOR)
112+
)
113+
}
114+
}
115+
100116
companion object {
101117
private const val EMPTY_STRING = ""
118+
private const val RUNNING_DATE_SEPARATOR = "."
102119
}
103120
}

β€Žpresentation/src/main/java/com/whyranoid/presentation/util/Extensions.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ fun Date.dateToString(format: String): String {
2626
return formatter.format(this)
2727
}
2828

29+
fun Long.toRunningDateString(): String {
30+
val formatter = SimpleDateFormat("yyyy.MM.dd", Locale.getDefault())
31+
return formatter.format(this)
32+
}
33+
2934
inline fun <reified T : Serializable> Intent.getSerializableData(key: String): T? = when {
3035
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getSerializableExtra(
3136
key,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item android:state_checked="true" android:color="@color/mogakrun_on_primary" />
4+
<item android:color="@color/mogakrun_bottom_navigation_menu_grey" />
5+
</selector>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<vector android:height="24dp" android:tint="@color/mogakrun_on_primary"
2+
android:viewportHeight="24" android:viewportWidth="24"
3+
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
4+
<path android:fillColor="@android:color/white" android:pathData="M16.59,7.58L10,14.17l-3.59,-3.58L5,12l5,5 8,-8zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
5+
</vector>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<shape xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:padding="10dp"
4+
android:shape="rectangle" >
5+
<solid android:color="@color/mogakrun_primary" />
6+
<corners
7+
android:bottomLeftRadius="12dp"
8+
android:bottomRightRadius="12dp"
9+
android:topLeftRadius="12dp"
10+
android:topRightRadius="12dp" />
11+
</shape>

0 commit comments

Comments
Β (0)