Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 카테고리, 일시 선택 순서 변경 및 리팩터링 (스타카토 생성/수정) #564 #567

Open
wants to merge 45 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bdc7aa3
build: 테스트를 위한 mockK 의존성 추가
s6m1n Dec 1, 2024
c30524f
build: InstantTaskExecutorRule 설정을 위한 androidx.arch.core 의존성 추가
s6m1n Dec 2, 2024
1a4fadc
refactor: MemoryCandidate 파일 분리 및 isDateWithinPeriod 추가
s6m1n Dec 3, 2024
7bb7a5f
build: 테스트 파라미터를 위한 junitparams 의존성 추가
s6m1n Dec 3, 2024
72d3462
test: MemoryCandidateTest 테스트 추가
s6m1n Dec 3, 2024
85218a1
test: MemoryCandidatesTest 테스트 추가
s6m1n Dec 3, 2024
fccfece
feat: MemoryCandidate에 getClosestDateTime 구현
s6m1n Dec 4, 2024
0e88c29
test: MemoryCandidate의 getClosestDateTime 테스트 추가
s6m1n Dec 4, 2024
7c0a30c
feat: MemoryCandidates에 getValidMemoryCandidate 메서드 오버로딩
s6m1n Dec 6, 2024
1c53970
test: MemoryCandidates의 getValidMemoryCandidate 메서드 테스트 추가
s6m1n Dec 6, 2024
f6f8592
ui: 스타카토 수정/생성 화면의 일시 선택과 추억 선택 순서 변경
s6m1n Dec 6, 2024
056df1c
refactor: getValidMemoryCandidate 메서드 명 filterCandidatesBy로 수정
s6m1n Dec 6, 2024
832f66f
refactor: MemoryCandidate의 getClosestDateTime 로직 수정 및 테스트 가독성 개선
s6m1n Dec 7, 2024
26213b5
refactor: VisitedAtSelectionFragment
s6m1n Dec 7, 2024
cf28140
refactor: 스타카토 생성 화면에서 일시, 추억 선택 순서 변경
s6m1n Dec 7, 2024
d876f13
test: StaccatoCreationViewModel 테스트 추가
s6m1n Dec 7, 2024
0c907ad
refactor: 스타카토 수정 화면에서 일시, 추억 선택 순서 변경
s6m1n Dec 7, 2024
b41c9d0
test: StaccatoUpdateViewModel 테스트 추가
s6m1n Dec 7, 2024
1ef8ed0
ui: 선택할 수 있는 추억이 없는 경우, 추억 선택 뷰 클릭 비활성화
s6m1n Dec 8, 2024
3f7602f
fix: setMemoryCandidateByVisitedAt 메서드 emptyList 예외로 인한 버그
s6m1n Dec 8, 2024
a8445a3
test: MainDispatcherRule 클래스 추가 및 적용
s6m1n Dec 8, 2024
8374921
ui: 불필요한 TextView, 바인딩 어댑터, stringRes 제거
s6m1n Dec 8, 2024
267c465
Merge branch 'develop' into feature/#564-refactor-category-datetime-s…
s6m1n Dec 8, 2024
605f5a6
style: updateMemoryCandidateAndVisitedAt 메서드 파라미터 명 수정
s6m1n Dec 8, 2024
5f82f21
test: StaccatoCreationViewModelTest에 runTest와 advanceUntilIdle 적용
s6m1n Dec 10, 2024
b97d4ac
test: StaccatoUpdateViewModelTest에 runTest와 advanceUntilIdle 적용
s6m1n Dec 11, 2024
db070ab
style: ktLintFormat
s6m1n Dec 13, 2024
bf63b30
refactor: 스타카토 생성/수정 activity의 observe 메서드 그룹화
s6m1n Dec 16, 2024
fc5b31d
refactor: Staccato 조회 화면에서 staccatoId를 불러올 수 없을 때 예외 반환
s6m1n Dec 16, 2024
24d9b73
refactor: 카테고리가 특정되지 않음을 의미하는 0L값 상수화
s6m1n Dec 16, 2024
458a76c
refactor: toLocalDate 메서드 중복 호출 개선
s6m1n Dec 16, 2024
67e73ee
refactor: changeTimeToNoon 메서드 분리
s6m1n Dec 16, 2024
9cd9f18
refactor: setSelectedMemory 메서드 if문 -> when문으로 변경
s6m1n Dec 16, 2024
2572bba
refactor: 스타카토 생성/수정 ViewModel 메서드명 단순화 (매개변수 정보 제거)
s6m1n Dec 16, 2024
b0097bb
refactor: MemoryCandidateTest에서 assertTrue, assertFalse 활용
s6m1n Dec 16, 2024
36fda43
refactor: MemoryCandidateTest 메서드명 개선 및 Parameters 추가
s6m1n Dec 16, 2024
ead1270
refactor: 테스트 코드 개선
s6m1n Dec 17, 2024
72a4471
fix: DEFAULT_STACCATO_ID 다시 추가
s6m1n Dec 18, 2024
311e26f
refactor: id가 파라미터인 findCandidatesBy의 반환값 변경
s6m1n Dec 18, 2024
1c6bc8e
style: 불필요한 it 제거 및 메서드 순서 변경
s6m1n Dec 18, 2024
09ccfcf
refactor: LocalDate 관련 테스트 픽스처 네이밍 변경
s6m1n Dec 18, 2024
e4a81fc
refactor: 테스트 코드 변수명, 함수 순서 변경
s6m1n Dec 18, 2024
4470102
refactor: 스타카토 생성, 수정 뷰모델 공통 로직 MemoryCandidates로 분리
s6m1n Dec 19, 2024
9b79f5a
test: MemoryCandidates - findByIdOrFirst 테스트 구현
s6m1n Dec 19, 2024
32bd428
refactor: setMemoryCandidateBy -> updateMemorySelectionBy 메서드명 변경
s6m1n Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion android/Staccato_AN/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ dependencies {
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
testImplementation(libs.androidx.arch.core)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

Expand Down Expand Up @@ -139,9 +140,15 @@ dependencies {
// Fragment
implementation(libs.androidx.fragment.ktx)

// Coroutines Test
testImplementation(libs.kotlinx.coroutines.test)

// Mockk
testImplementation(libs.mockk.android)
testImplementation(libs.junitparams)
testImplementation(libs.mockk)
testImplementation(libs.mockk.agent)
androidTestImplementation(libs.mockk.agent)
androidTestImplementation(libs.mockk.android)

// Navigation
implementation(libs.androidx.navigation.fragment.ktx)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
package com.on.staccato.domain.model

import java.time.LocalDate
import java.time.LocalDateTime
import java.time.YearMonth

data class MemoryCandidates(val memoryCandidate: List<MemoryCandidate>)

data class MemoryCandidate(
val memoryId: Long,
val memoryTitle: String,
val startAt: LocalDate?,
val endAt: LocalDate?,
) {
fun getClosestDateTime(date: LocalDateTime): LocalDateTime {
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
if (startAt == null || endAt == null) return date

val startDateTime = startAt.atStartOfDay()
val endDateTime = endAt.atTime(23, 59)

return when {
date.isBefore(startDateTime) ->
startDateTime.toLocalDate()
.atTime(12, 0)
date.isAfter(endDateTime) ->
endDateTime.toLocalDate()
.atTime(12, 0)
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
else -> date
}
}
s6m1n marked this conversation as resolved.
Show resolved Hide resolved

fun isDateWithinPeriod(date: LocalDate): Boolean {
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
if (startAt == null || endAt == null) return true
return date in startAt..endAt
}

companion object {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MemoryCandidate에 따라 스타카토의 날짜 범위가 달라지는 것은 맞지만, 이와 관련된 모든 로직을 MemoryCandidate의 동반 객체가 가지고 있는 점이 개인적으로 조금 어색하게 느껴졌습니다..!
동반 객체가 많은 책임을 가지고 있는 것 같아서, 스타카토의 날짜 후보를 선정하는 객체를 만들어 동반 객체의 책임을 조금 분산 시켜도 좋을 것 같다는 생각이 들었어요~

스타카토의 날짜 범위를 설정하는 로직을 MemoryCandidate의 동반 객체가 모두 가지고 있도록 구현하신 이유가 있으셨나요? 단순히 빙티의 의견이 궁금해서 여쭤봅니당! 😊

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private fun getAvailableMonths(
    year: Int,
    startAt: LocalDate,
    endAt: LocalDate,
): List<Int> {
    return when {
        year == startAt.year && year == endAt.year -> (startAt.monthValue..endAt.monthValue).toList()
        year == startAt.year -> (startAt.monthValue..12).toList()
        year == endAt.year -> (1..endAt.monthValue).toList()
        else -> (1..12).toList()
    }
}

getAvailableMonths() 메서드 내 when 문의 조건을 정리해보면 아래와 같은 규칙을 가지고 있어요!

  • year == startAt.year ➡️ startAt.monthValue 반환
  • year != startAt.year ➡️ 1 반환
  • year == endAt.year ➡️ endAt.monthValue 반환
  • year != endAt.year ➡️ 12 반환

위 규칙을 활용해 조건에 따라 startMonthendMonth를 각각 계산한 후에 list를 만든다면 가독성이 좀 더 좋아질 것 같아요! 빙티의 생각은 어떠신가요? 😄

private fun getAvailableMonths(
    year: Int,
    startAt: LocalDate,
    endAt: LocalDate,
): List<Int> {
    val startMonth = if (year == startAt.year) startAt.monthValue else 1
    val endMonth = if (year == endAt.year) endAt.monthValue else 12

    return (startMonth..endMonth).toList()
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private fun getAvailableDays(
    year: Int,
    month: Int,
    startAt: LocalDate,
    endAt: LocalDate,
): List<Int> {
    val yearMonth = YearMonth.of(year, month)
    val daysInMonth = yearMonth.lengthOfMonth()
    return when {
        year == startAt.year && month == startAt.monthValue && year == endAt.year && month == endAt.monthValue -> {
            (startAt.dayOfMonth..endAt.dayOfMonth).toList()
        }
        year == startAt.year && month == startAt.monthValue -> (startAt.dayOfMonth..daysInMonth).toList()
        year == endAt.year && month == endAt.monthValue -> (1..endAt.dayOfMonth).toList()
        else -> (1..daysInMonth).toList()
    }
}

getAvailableDays()역시 위 코멘트와 동일한 의견입니다!
getAvailableDays() 메서드도 when 문의 조건을 정리해보면 아래와 같은 규칙을 가지고 있어요!

  • year == startAt.year && month == startAt.monthValue 이면 startAt.dayOfMonth 반환, 아니면 1 반환
  • year == endAt.year && month == endAt.monthValue 이면 endAt.dayOfMonth 반환, 아니면 daysInMonth 반환

따라서 위 규칙을 활용해 아래와 같이 코드를 수정하면 좀 더 가독성이 좋아질 것 같아요!

private fun getAvailableDays(
    year: Int,
    month: Int,
    startAt: LocalDate,
    endAt: LocalDate,
): List<Int> {
    val yearMonth = YearMonth.of(year, month)
    val daysInMonth = yearMonth.lengthOfMonth()

    val startDay = if (year == startAt.year && month == startAt.monthValue) startAt.dayOfMonth else 1
    val endDay = if (year == endAt.year && month == endAt.monthValue) endAt.dayOfMonth else daysInMonth

    return (startDay..endDay).toList()
}

Copy link
Contributor

@hxeyexn hxeyexn Dec 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAvailableYears(), getDefaultYears(), getDefaultMonths() 메서드는 표현식을 사용해도 좋을 것 같네요!

ex)

private fun getAvailableYears(
    startAt: LocalDate,
    endAt: LocalDate,
): List<Int> = (startAt.year..endAt.year).toList()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fun buildNumberPickerDates(
    startAt: LocalDate?,
    endAt: LocalDate?,
): Map<Int, Map<Int, List<Int>>> {
    ...
}

buildNumberPickerDates() 메서드 내부 로직을 뜯어보지 않는 이상 Map<Int, Map<Int, List<Int>>>이 의미를 추론하기 어려울 것 같아요..! typealias를 활용해 반환값을 좀 더 명시적으로 나타내보는 건 어떨까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildAvailableDates()은 로직을 보고 반환값을 판단하기 어려우니 반환값을 명시해주면 좋을 것 같아요!

fun buildNumberPickerDates(
startAt: LocalDate?,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.on.staccato.domain.model

import java.time.LocalDate

data class MemoryCandidates(val memoryCandidate: List<MemoryCandidate>) {
fun filterCandidatesBy(date: LocalDate): List<MemoryCandidate> = memoryCandidate.filter { it.isDateWithinPeriod(date) }

fun filterCandidatesBy(memoryId: Long): List<MemoryCandidate> = memoryCandidate.filter { it.memoryId == memoryId }
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
}
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,31 @@ fun TextView.setSelectedMemory(
memoryCandidates: MemoryCandidates?,
) {
if (memoryCandidates?.memoryCandidate?.isEmpty() == true) {
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
text = resources.getString(R.string.staccato_creation_no_memory_hint)
text = resources.getString(R.string.staccato_creation_no_memory)
setTextColor(resources.getColor(R.color.gray3, null))
isClickable = false
isFocusable = false
} else if (selectedMemory == null) {
text = resources.getString(R.string.staccato_creation_memory_selection_hint)
setTextColor(resources.getColor(R.color.gray3, null))
isClickable = true
isFocusable = true
} else {
text = selectedMemory.memoryTitle
setTextColor(resources.getColor(R.color.staccato_black, null))
}
}

@BindingAdapter(value = ["dateTimeWithAmPm", "memoryCandidates"])
fun TextView.setDateTimeWithAmPm(
dateTime: LocalDateTime?,
memoryCandidates: MemoryCandidates?,
) {
if (memoryCandidates?.memoryCandidate?.isEmpty() == true) {
text = resources.getString(R.string.staccato_creation_memory_selection_hint)
text = resources.getString(R.string.staccato_creation_no_memory_in_this_date)
setTextColor(resources.getColor(R.color.gray3, null))
isClickable = false
isFocusable = false
} else {
text = dateTime?.let(::getFormattedLocalDateTime) ?: EMPTY_TEXT
setTextColor(resources.getColor(R.color.staccato_black, null))
text = selectedMemory.memoryTitle
isClickable = true
isFocusable = true
setTextColor(resources.getColor(R.color.staccato_black, null))
}
}

@BindingAdapter("dateTimeWithAmPm")
fun TextView.setDateTimeWithAmPm(dateTime: LocalDateTime?) {
text = dateTime?.let(::getFormattedLocalDateTime) ?: EMPTY_TEXT
setTextColor(resources.getColor(R.color.staccato_black, null))
isClickable = true
isFocusable = true
}

@BindingAdapter(
value = ["startDate", "endDate"],
)
Expand All @@ -73,11 +65,6 @@ fun TextView.setIsMemoryCandidatesEmptyVisibility(memoryCandidates: MemoryCandid
isGone = !memoryCandidates?.memoryCandidate.isNullOrEmpty()
}

@BindingAdapter("isMemoryEmpty")
fun TextView.setIsMemoryEmptyVisibility(items: List<Int>?) {
isGone = !items.isNullOrEmpty()
}

@BindingAdapter("visitedAtHistory")
fun TextView.formatVisitedAtHistory(visitedAt: LocalDateTime?) {
text = visitedAt?.let {
Expand Down
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import com.on.staccato.presentation.util.showToast
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.time.LocalDate
import java.time.LocalDateTime

@AndroidEntryPoint
Expand Down Expand Up @@ -87,7 +86,7 @@ class StaccatoCreationActivity :
private var currentSnackBar: Snackbar? = null

override fun initStartView(savedInstanceState: Bundle?) {
viewModel.fetchMemoryCandidates(memoryId)
viewModel.fetchMemoryCandidates()
setupPermissionRequestLauncher()
setupFusedLocationProviderClient()
initBinding()
Expand Down Expand Up @@ -269,10 +268,6 @@ class StaccatoCreationActivity :

private fun initMemorySelectionFragment() {
memorySelectionFragment.setOnMemorySelected { selectedMemory ->
val startAt = selectedMemory.startAt ?: LocalDate.now()
val initializedDateTime =
LocalDateTime.of(startAt.year, startAt.month, startAt.dayOfMonth, 0, 0, 0)
viewModel.selectedVisitedAt(initializedDateTime)
viewModel.selectMemory(selectedMemory)
}
}
Expand Down Expand Up @@ -302,18 +297,32 @@ class StaccatoCreationActivity :
)
}
viewModel.memoryCandidates.observe(this) {
memorySelectionFragment.setItems(it.memoryCandidate)
it?.let {
viewModel.initMemoryAndVisitedAt(memoryId, LocalDateTime.now())
}
}
viewModel.selectableMemories.observe(this) {
it?.let {
memorySelectionFragment.setItems(it)
}
}
viewModel.selectedMemory.observe(this) { selectedMemory ->
val startAt = selectedMemory.startAt ?: LocalDate.now()
val initializedDateTime =
LocalDateTime.of(startAt.year, startAt.month, startAt.dayOfMonth, 0, 0, 0)
memorySelectionFragment.updateKeyMemory(selectedMemory)
visitedAtSelectionFragment.initDateCandidates(selectedMemory, initializedDateTime)
viewModel.selectedMemory.observe(this) { it ->
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
it?.let {
memorySelectionFragment.updateKeyMemory(it)
if (memoryId != 0L) {
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
visitedAtSelectionFragment.setVisitedAtPeriod(
it.startAt,
it.endAt,
)
}
}
}
viewModel.selectedVisitedAt.observe(this) { selectedVisitedAt ->
if (selectedVisitedAt != null) {
visitedAtSelectionFragment.initKeyWithSelectedValues(selectedVisitedAt)
viewModel.selectedVisitedAt.observe(this) {
it?.let {
visitedAtSelectionFragment.updateSelectedVisitedAt(it)
if (memoryId == 0L) {
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
updateMemoryCandidateAndVisitedAt(it)
}
}
}
viewModel.createdStaccatoId.observe(this) { createdStaccatoId ->
Expand All @@ -328,6 +337,14 @@ class StaccatoCreationActivity :
}
}

private fun updateMemoryCandidateAndVisitedAt(visitedAt: LocalDateTime) {
viewModel.setMemoryCandidateByVisitedAt(visitedAt)
visitedAtSelectionFragment.setVisitedAtPeriod(
visitedAt.toLocalDate().minusYears(10),
visitedAt.toLocalDate().plusYears(10),
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
)
s6m1n marked this conversation as resolved.
Show resolved Hide resolved
}

private fun fetchAddress(location: Location) {
lifecycleScope.launch {
val defaultDelayJob =
Expand Down
Loading
Loading