diff --git a/build-logic/convention/src/main/kotlin/com/twix/convention/FeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/com/twix/convention/FeatureConventionPlugin.kt
index 8bf351be..8597f405 100644
--- a/build-logic/convention/src/main/kotlin/com/twix/convention/FeatureConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/com/twix/convention/FeatureConventionPlugin.kt
@@ -15,6 +15,7 @@ class FeatureConventionPlugin : BuildLogicConventionPlugin({
implementation(project(":core:design-system"))
implementation(project(":core:navigation"))
implementation(project(":core:ui"))
+ implementation(project(":core:result"))
implementation(project(":domain"))
}
})
diff --git a/core/design-system/src/main/java/com/twix/designsystem/extension/GoalIconType.kt b/core/design-system/src/main/java/com/twix/designsystem/extension/GoalIconType.kt
new file mode 100644
index 00000000..65e04aa3
--- /dev/null
+++ b/core/design-system/src/main/java/com/twix/designsystem/extension/GoalIconType.kt
@@ -0,0 +1,16 @@
+package com.twix.designsystem.extension
+
+import com.twix.designsystem.R
+import com.twix.domain.model.enums.GoalIconType
+
+fun GoalIconType.toRes(): Int =
+ when (this) {
+ GoalIconType.DEFAULT -> R.drawable.ic_default
+ GoalIconType.CLEAN -> R.drawable.ic_clean
+ GoalIconType.EXERCISE -> R.drawable.ic_exercise
+ GoalIconType.BOOK -> R.drawable.ic_book
+ GoalIconType.PENCIL -> R.drawable.ic_pencil
+ GoalIconType.HEALTH -> R.drawable.ic_health
+ GoalIconType.HEART -> R.drawable.ic_heart
+ GoalIconType.LAPTOP -> R.drawable.ic_laptop
+ }
diff --git a/core/design-system/src/main/res/drawable/ic_checked_me.xml b/core/design-system/src/main/res/drawable/ic_checked_me.xml
new file mode 100644
index 00000000..992974fe
--- /dev/null
+++ b/core/design-system/src/main/res/drawable/ic_checked_me.xml
@@ -0,0 +1,18 @@
+
+
+
+
diff --git a/core/design-system/src/main/res/drawable/ic_checked_you.xml b/core/design-system/src/main/res/drawable/ic_checked_you.xml
new file mode 100644
index 00000000..495fd1ab
--- /dev/null
+++ b/core/design-system/src/main/res/drawable/ic_checked_you.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/core/design-system/src/main/res/drawable/ic_unchecked_me.xml b/core/design-system/src/main/res/drawable/ic_unchecked_me.xml
new file mode 100644
index 00000000..215461a0
--- /dev/null
+++ b/core/design-system/src/main/res/drawable/ic_unchecked_me.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/core/design-system/src/main/res/drawable/ic_unchecked_you.xml b/core/design-system/src/main/res/drawable/ic_unchecked_you.xml
new file mode 100644
index 00000000..4b9defc5
--- /dev/null
+++ b/core/design-system/src/main/res/drawable/ic_unchecked_you.xml
@@ -0,0 +1,18 @@
+
+
+
+
diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml
index 657bea88..c9901cfa 100644
--- a/core/design-system/src/main/res/values/strings.xml
+++ b/core/design-system/src/main/res/values/strings.xml
@@ -37,5 +37,6 @@
종료 날짜가 시작 날짜보다 이전입니다.
+ 목표 조회에 실패했습니다.
\ No newline at end of file
diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts
index 5a301264..ae951421 100644
--- a/core/network/build.gradle.kts
+++ b/core/network/build.gradle.kts
@@ -42,6 +42,7 @@ android {
dependencies {
implementation(projects.core.token)
implementation(projects.core.result)
+ implementation(projects.domain)
implementation(libs.bundles.ktor)
implementation(libs.ktorfit.lib)
diff --git a/core/network/src/main/java/com/twix/network/di/ApiServiceModule.kt b/core/network/src/main/java/com/twix/network/di/ApiServiceModule.kt
index f911f84f..254a0387 100644
--- a/core/network/src/main/java/com/twix/network/di/ApiServiceModule.kt
+++ b/core/network/src/main/java/com/twix/network/di/ApiServiceModule.kt
@@ -1,8 +1,10 @@
package com.twix.network.di
import com.twix.network.service.AuthService
+import com.twix.network.service.GoalService
import com.twix.network.service.OnboardingService
import com.twix.network.service.createAuthService
+import com.twix.network.service.createGoalService
import com.twix.network.service.createOnboardingService
import de.jensklingenberg.ktorfit.Ktorfit
import org.koin.dsl.module
@@ -15,4 +17,7 @@ internal val apiServiceModule =
single {
get().createAuthService()
}
+ single {
+ get().createGoalService()
+ }
}
diff --git a/core/network/src/main/java/com/twix/network/model/response/goal/mapper/GoalMapper.kt b/core/network/src/main/java/com/twix/network/model/response/goal/mapper/GoalMapper.kt
new file mode 100644
index 00000000..715c671d
--- /dev/null
+++ b/core/network/src/main/java/com/twix/network/model/response/goal/mapper/GoalMapper.kt
@@ -0,0 +1,38 @@
+package com.twix.network.model.response.goal.mapper
+
+import com.twix.domain.model.enums.GoalIconType
+import com.twix.domain.model.enums.RepeatCycle
+import com.twix.domain.model.goal.Goal
+import com.twix.domain.model.goal.GoalList
+import com.twix.domain.model.goal.GoalVerification
+import com.twix.network.model.response.goal.model.GoalListResponse
+import com.twix.network.model.response.goal.model.GoalResponse
+import com.twix.network.model.response.goal.model.VerificationResponse
+
+fun GoalListResponse.toDomain(): GoalList =
+ GoalList(
+ completedCount = completedCount,
+ totalCount = totalCount,
+ goals = goals.map { it.toDomain() },
+ )
+
+fun GoalResponse.toDomain(): Goal =
+ Goal(
+ goalId = goalId,
+ name = name,
+ icon = GoalIconType.fromApi(icon),
+ repeatCycle = RepeatCycle.fromApi(repeatCycle),
+ myCompleted = myCompleted,
+ partnerCompleted = partnerCompleted,
+ myVerification = myVerification?.toDomainOrNull(),
+ partnerVerification = partnerVerification?.toDomainOrNull(),
+ )
+
+fun VerificationResponse.toDomainOrNull(): GoalVerification? =
+ GoalVerification(
+ photologId = photologId,
+ imageUrl = imageUrl,
+ comment = comment,
+ reaction = reaction,
+ uploadedAt = uploadedAt,
+ )
diff --git a/core/network/src/main/java/com/twix/network/model/response/goal/model/GoalListResponse.kt b/core/network/src/main/java/com/twix/network/model/response/goal/model/GoalListResponse.kt
new file mode 100644
index 00000000..caa4f0aa
--- /dev/null
+++ b/core/network/src/main/java/com/twix/network/model/response/goal/model/GoalListResponse.kt
@@ -0,0 +1,11 @@
+package com.twix.network.model.response.goal.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class GoalListResponse(
+ @SerialName("completedCount") val completedCount: Int,
+ @SerialName("totalCount") val totalCount: Int,
+ @SerialName("goals") val goals: List,
+)
diff --git a/core/network/src/main/java/com/twix/network/model/response/goal/model/GoalResponse.kt b/core/network/src/main/java/com/twix/network/model/response/goal/model/GoalResponse.kt
new file mode 100644
index 00000000..e35d9047
--- /dev/null
+++ b/core/network/src/main/java/com/twix/network/model/response/goal/model/GoalResponse.kt
@@ -0,0 +1,16 @@
+package com.twix.network.model.response.goal.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class GoalResponse(
+ @SerialName("goalId") val goalId: Long,
+ @SerialName("name") val name: String,
+ @SerialName("icon") val icon: String,
+ @SerialName("repeatCycle") val repeatCycle: String,
+ @SerialName("myCompleted") val myCompleted: Boolean,
+ @SerialName("partnerCompleted") val partnerCompleted: Boolean,
+ @SerialName("myVerification") val myVerification: VerificationResponse? = null,
+ @SerialName("partnerVerification") val partnerVerification: VerificationResponse? = null,
+)
diff --git a/core/network/src/main/java/com/twix/network/model/response/goal/model/VerificationResponse.kt b/core/network/src/main/java/com/twix/network/model/response/goal/model/VerificationResponse.kt
new file mode 100644
index 00000000..385048dc
--- /dev/null
+++ b/core/network/src/main/java/com/twix/network/model/response/goal/model/VerificationResponse.kt
@@ -0,0 +1,13 @@
+package com.twix.network.model.response.goal.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class VerificationResponse(
+ @SerialName("photologId") val photologId: Long,
+ @SerialName("imageUrl") val imageUrl: String,
+ @SerialName("comment") val comment: String? = null,
+ @SerialName("reaction") val reaction: String? = null,
+ @SerialName("uploadedAt") val uploadedAt: String, // yyyy-mm-dd
+)
diff --git a/core/network/src/main/java/com/twix/network/service/AuthService.kt b/core/network/src/main/java/com/twix/network/service/AuthService.kt
index 6f87570b..d3981030 100644
--- a/core/network/src/main/java/com/twix/network/service/AuthService.kt
+++ b/core/network/src/main/java/com/twix/network/service/AuthService.kt
@@ -8,12 +8,12 @@ import de.jensklingenberg.ktorfit.http.Body
import de.jensklingenberg.ktorfit.http.POST
interface AuthService {
- @POST("auth/google/token")
+ @POST("api/v1/auth/google/token")
suspend fun googleLogin(
@Body request: LoginRequest,
): LoginResponse
- @POST("auth/refresh")
+ @POST("api/v1/auth/refresh")
suspend fun refresh(
@Body request: RefreshRequest,
): RefreshResponse
diff --git a/core/network/src/main/java/com/twix/network/service/GoalService.kt b/core/network/src/main/java/com/twix/network/service/GoalService.kt
new file mode 100644
index 00000000..8ff116e7
--- /dev/null
+++ b/core/network/src/main/java/com/twix/network/service/GoalService.kt
@@ -0,0 +1,12 @@
+package com.twix.network.service
+
+import com.twix.network.model.response.goal.model.GoalListResponse
+import de.jensklingenberg.ktorfit.http.GET
+import de.jensklingenberg.ktorfit.http.Query
+
+interface GoalService {
+ @GET("api/v1/goals")
+ suspend fun fetchGoals(
+ @Query("date") date: String,
+ ): GoalListResponse
+}
diff --git a/core/result/build.gradle.kts b/core/result/build.gradle.kts
index 2711e586..ba7b83cc 100644
--- a/core/result/build.gradle.kts
+++ b/core/result/build.gradle.kts
@@ -1,7 +1,3 @@
plugins {
- alias(libs.plugins.twix.android.library)
-}
-
-android {
- namespace = "com.twix.result"
+ alias(libs.plugins.twix.java.library)
}
diff --git a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt
index f240411a..09b49864 100644
--- a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt
+++ b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt
@@ -81,7 +81,7 @@ abstract class BaseViewModel(
onStart: (() -> Unit)? = null, // 비동기 시작 전 처리해야 할 로직 ex) 로딩
onFinally: (() -> Unit)? = null, // 비동기 종료 후 리소스 정리
onSuccess: (D) -> Unit, // 비동기 메서드 호출이 성공했을 때 처리해야 할 로직
- onError: ((AppError) -> Unit)? = null, // 비동기 메서드 호출에 실패했을 때 처리해야 할 로직
+ onError: (suspend (AppError) -> Unit)? = null, // 비동기 메서드 호출에 실패했을 때 처리해야 할 로직
block: suspend () -> AppResult, // 비동기 메서드 ex) 서버 통신 메서드
): Job =
viewModelScope.launch {
diff --git a/data/build.gradle.kts b/data/build.gradle.kts
index a2d22455..06467ea1 100644
--- a/data/build.gradle.kts
+++ b/data/build.gradle.kts
@@ -8,5 +8,6 @@ android {
dependencies {
implementation(projects.core.datastore)
+ implementation(projects.core.result)
implementation(projects.core.token)
}
diff --git a/data/src/main/java/com/twix/data/di/RepositoryModule.kt b/data/src/main/java/com/twix/data/di/RepositoryModule.kt
index a3d7bd3c..6fb9e27c 100644
--- a/data/src/main/java/com/twix/data/di/RepositoryModule.kt
+++ b/data/src/main/java/com/twix/data/di/RepositoryModule.kt
@@ -1,8 +1,10 @@
package com.twix.data.di
import com.twix.data.repository.DefaultAuthRepository
+import com.twix.data.repository.DefaultGoalRepository
import com.twix.data.repository.DefaultOnboardingRepository
import com.twix.domain.repository.AuthRepository
+import com.twix.domain.repository.GoalRepository
import com.twix.domain.repository.OnBoardingRepository
import org.koin.dsl.module
@@ -11,6 +13,9 @@ internal val repositoryModule =
single {
DefaultOnboardingRepository(get())
}
+ single {
+ DefaultGoalRepository(get())
+ }
single {
DefaultAuthRepository(get(), get())
}
diff --git a/data/src/main/java/com/twix/data/repository/DefaultGoalRepository.kt b/data/src/main/java/com/twix/data/repository/DefaultGoalRepository.kt
new file mode 100644
index 00000000..f145b393
--- /dev/null
+++ b/data/src/main/java/com/twix/data/repository/DefaultGoalRepository.kt
@@ -0,0 +1,14 @@
+package com.twix.data.repository
+
+import com.twix.domain.model.goal.GoalList
+import com.twix.domain.repository.GoalRepository
+import com.twix.network.execute.safeApiCall
+import com.twix.network.model.response.goal.mapper.toDomain
+import com.twix.network.service.GoalService
+import com.twix.result.AppResult
+
+class DefaultGoalRepository(
+ private val service: GoalService,
+) : GoalRepository {
+ override suspend fun fetchGoalList(date: String): AppResult = safeApiCall { service.fetchGoals(date).toDomain() }
+}
diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts
index ba7b83cc..e39ea89d 100644
--- a/domain/build.gradle.kts
+++ b/domain/build.gradle.kts
@@ -1,3 +1,7 @@
plugins {
alias(libs.plugins.twix.java.library)
}
+
+dependencies {
+ implementation(projects.core.result)
+}
diff --git a/domain/src/main/java/com/twix/domain/model/enums/GoalIconType.kt b/domain/src/main/java/com/twix/domain/model/enums/GoalIconType.kt
index 79abb906..0f4870e4 100644
--- a/domain/src/main/java/com/twix/domain/model/enums/GoalIconType.kt
+++ b/domain/src/main/java/com/twix/domain/model/enums/GoalIconType.kt
@@ -9,4 +9,20 @@ enum class GoalIconType {
HEALTH,
HEART,
LAPTOP,
+ ;
+
+ companion object {
+ fun fromApi(icon: String): GoalIconType =
+ when (icon) {
+ "ICON_DEFAULT" -> DEFAULT
+ "ICON_CLEAN" -> CLEAN
+ "ICON_EXERCISE" -> EXERCISE
+ "ICON_BOOK" -> BOOK
+ "ICON_PENCIL" -> PENCIL
+ "ICON_HEALTH" -> HEALTH
+ "ICON_HEART" -> HEART
+ "ICON_LAPTOP" -> LAPTOP
+ else -> DEFAULT
+ }
+ }
}
diff --git a/domain/src/main/java/com/twix/domain/model/enums/RepeatCycle.kt b/domain/src/main/java/com/twix/domain/model/enums/RepeatCycle.kt
new file mode 100644
index 00000000..2931eaf9
--- /dev/null
+++ b/domain/src/main/java/com/twix/domain/model/enums/RepeatCycle.kt
@@ -0,0 +1,12 @@
+package com.twix.domain.model.enums
+
+enum class RepeatCycle {
+ DAILY,
+ WEEKLY,
+ MONTHLY,
+ ;
+
+ companion object {
+ fun fromApi(value: String): RepeatCycle = runCatching { valueOf(value) }.getOrElse { DAILY }
+ }
+}
diff --git a/domain/src/main/java/com/twix/domain/model/enums/RepeatType.kt b/domain/src/main/java/com/twix/domain/model/enums/RepeatType.kt
deleted file mode 100644
index 61a44e25..00000000
--- a/domain/src/main/java/com/twix/domain/model/enums/RepeatType.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.twix.domain.model.enums
-
-enum class RepeatType {
- DAILY,
- WEEKLY,
- MONTHLY,
-}
diff --git a/domain/src/main/java/com/twix/domain/model/goal/Goal.kt b/domain/src/main/java/com/twix/domain/model/goal/Goal.kt
new file mode 100644
index 00000000..a4d03f14
--- /dev/null
+++ b/domain/src/main/java/com/twix/domain/model/goal/Goal.kt
@@ -0,0 +1,15 @@
+package com.twix.domain.model.goal
+
+import com.twix.domain.model.enums.GoalIconType
+import com.twix.domain.model.enums.RepeatCycle
+
+data class Goal(
+ val goalId: Long,
+ val name: String,
+ val icon: GoalIconType,
+ val repeatCycle: RepeatCycle,
+ val myCompleted: Boolean,
+ val partnerCompleted: Boolean,
+ val myVerification: GoalVerification?,
+ val partnerVerification: GoalVerification?,
+)
diff --git a/domain/src/main/java/com/twix/domain/model/goal/GoalList.kt b/domain/src/main/java/com/twix/domain/model/goal/GoalList.kt
new file mode 100644
index 00000000..2ff8a336
--- /dev/null
+++ b/domain/src/main/java/com/twix/domain/model/goal/GoalList.kt
@@ -0,0 +1,7 @@
+package com.twix.domain.model.goal
+
+data class GoalList(
+ val completedCount: Int = 0,
+ val totalCount: Int = 0,
+ val goals: List = emptyList(),
+)
diff --git a/domain/src/main/java/com/twix/domain/model/goal/GoalVerification.kt b/domain/src/main/java/com/twix/domain/model/goal/GoalVerification.kt
new file mode 100644
index 00000000..c9367ad7
--- /dev/null
+++ b/domain/src/main/java/com/twix/domain/model/goal/GoalVerification.kt
@@ -0,0 +1,9 @@
+package com.twix.domain.model.goal
+
+data class GoalVerification(
+ val photologId: Long,
+ val imageUrl: String,
+ val comment: String?,
+ val reaction: String?,
+ val uploadedAt: String,
+)
diff --git a/domain/src/main/java/com/twix/domain/repository/GoalRepository.kt b/domain/src/main/java/com/twix/domain/repository/GoalRepository.kt
new file mode 100644
index 00000000..be0958f9
--- /dev/null
+++ b/domain/src/main/java/com/twix/domain/repository/GoalRepository.kt
@@ -0,0 +1,8 @@
+package com.twix.domain.repository
+
+import com.twix.domain.model.goal.GoalList
+import com.twix.result.AppResult
+
+interface GoalRepository {
+ suspend fun fetchGoalList(date: String): AppResult
+}
diff --git a/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorIntent.kt b/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorIntent.kt
index 0ac5656b..c6cb00f2 100644
--- a/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorIntent.kt
+++ b/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorIntent.kt
@@ -1,7 +1,7 @@
package com.twix.goal_editor
import com.twix.domain.model.enums.GoalIconType
-import com.twix.domain.model.enums.RepeatType
+import com.twix.domain.model.enums.RepeatCycle
import com.twix.ui.base.Intent
import java.time.LocalDate
@@ -15,7 +15,7 @@ sealed interface GoalEditorIntent : Intent {
) : GoalEditorIntent
data class SetRepeatType(
- val repeatType: RepeatType,
+ val repeatCycle: RepeatCycle,
) : GoalEditorIntent
data class SetRepeatCount(
diff --git a/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorScreen.kt b/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorScreen.kt
index e3ec4af9..13fce06a 100644
--- a/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorScreen.kt
+++ b/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorScreen.kt
@@ -46,18 +46,18 @@ import com.twix.designsystem.components.dialog.CommonDialog
import com.twix.designsystem.components.text.AppText
import com.twix.designsystem.components.toast.ToastManager
import com.twix.designsystem.components.toast.model.ToastData
+import com.twix.designsystem.extension.toRes
import com.twix.designsystem.theme.CommonColor
import com.twix.designsystem.theme.GrayColor
import com.twix.designsystem.theme.TwixTheme
import com.twix.domain.model.enums.AppTextStyle
import com.twix.domain.model.enums.GoalIconType
-import com.twix.domain.model.enums.RepeatType
+import com.twix.domain.model.enums.RepeatCycle
import com.twix.goal_editor.component.EmojiPicker
import com.twix.goal_editor.component.GoalEditorTopBar
import com.twix.goal_editor.component.GoalInfoCard
import com.twix.goal_editor.component.GoalTextField
import com.twix.goal_editor.component.label
-import com.twix.goal_editor.component.toRes
import com.twix.goal_editor.model.GoalEditorUiState
import com.twix.ui.extension.dismissKeyboardOnTap
import com.twix.ui.extension.noRippleClickable
@@ -110,7 +110,7 @@ fun GoalEditorScreen(
isEdit: Boolean = false,
onBack: () -> Unit,
onCommitTitle: (String) -> Unit,
- onSelectRepeatType: (RepeatType) -> Unit,
+ onSelectRepeatType: (RepeatCycle) -> Unit,
onCommitIcon: (GoalIconType) -> Unit,
onCommitEndDate: (LocalDate) -> Unit,
onCommitStartDate: (LocalDate) -> Unit,
@@ -155,7 +155,7 @@ fun GoalEditorScreen(
Spacer(Modifier.height(44.dp))
GoalInfoCard(
- selectedRepeatType = uiState.selectedRepeatType,
+ selectedRepeatCycle = uiState.selectedRepeatCycle,
repeatCount = uiState.repeatCount,
startDate = uiState.startDate,
endDateEnabled = uiState.endDateEnabled,
@@ -204,7 +204,7 @@ fun GoalEditorScreen(
content = {
RepeatCountBottomSheetContent(
repeatCount = uiState.repeatCount,
- selectedRepeatType = uiState.selectedRepeatType,
+ selectedRepeatCycle = uiState.selectedRepeatCycle,
onCommit = { repeatType, repeatCount ->
onSelectRepeatType(repeatType)
onCommitRepeatCount(repeatCount)
@@ -289,12 +289,12 @@ private fun IconEditorDialogContent(
@Composable
private fun RepeatCountBottomSheetContent(
repeatCount: Int,
- selectedRepeatType: RepeatType,
- onCommit: (RepeatType, Int) -> Unit,
+ selectedRepeatCycle: RepeatCycle,
+ onCommit: (RepeatCycle, Int) -> Unit,
) {
var internalRepeatCount by remember { mutableIntStateOf(repeatCount) }
- var internalSelectedRepeatType by remember { mutableStateOf(selectedRepeatType) }
- val maxCount = if (internalSelectedRepeatType == RepeatType.WEEKLY) 6 else 25
+ var internalSelectedRepeatType by remember { mutableStateOf(selectedRepeatCycle) }
+ val maxCount = if (internalSelectedRepeatType == RepeatCycle.WEEKLY) 6 else 25
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@@ -306,33 +306,33 @@ private fun RepeatCountBottomSheetContent(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
AppText(
- text = RepeatType.WEEKLY.label(),
+ text = RepeatCycle.WEEKLY.label(),
style = AppTextStyle.B2,
- color = if (internalSelectedRepeatType == RepeatType.WEEKLY) CommonColor.White else GrayColor.C500,
+ color = if (internalSelectedRepeatType == RepeatCycle.WEEKLY) CommonColor.White else GrayColor.C500,
modifier =
Modifier
.clip(RoundedCornerShape(8.dp))
- .background(if (internalSelectedRepeatType == RepeatType.WEEKLY) GrayColor.C500 else CommonColor.White)
+ .background(if (internalSelectedRepeatType == RepeatCycle.WEEKLY) GrayColor.C500 else CommonColor.White)
.border(1.dp, GrayColor.C500, RoundedCornerShape(8.dp))
.padding(horizontal = 12.dp, vertical = 5.5.dp)
.noRippleClickable(onClick = {
- internalSelectedRepeatType = RepeatType.WEEKLY
+ internalSelectedRepeatType = RepeatCycle.WEEKLY
internalRepeatCount = 0
}),
)
AppText(
- text = RepeatType.MONTHLY.label(),
+ text = RepeatCycle.MONTHLY.label(),
style = AppTextStyle.B2,
- color = if (internalSelectedRepeatType == RepeatType.MONTHLY) CommonColor.White else GrayColor.C500,
+ color = if (internalSelectedRepeatType == RepeatCycle.MONTHLY) CommonColor.White else GrayColor.C500,
modifier =
Modifier
.clip(RoundedCornerShape(8.dp))
- .background(if (internalSelectedRepeatType == RepeatType.MONTHLY) GrayColor.C500 else CommonColor.White)
+ .background(if (internalSelectedRepeatType == RepeatCycle.MONTHLY) GrayColor.C500 else CommonColor.White)
.border(1.dp, GrayColor.C500, RoundedCornerShape(8.dp))
.padding(horizontal = 12.dp, vertical = 5.5.dp)
.noRippleClickable(onClick = {
- internalSelectedRepeatType = RepeatType.MONTHLY
+ internalSelectedRepeatType = RepeatCycle.MONTHLY
internalRepeatCount = 0
}),
)
diff --git a/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorViewModel.kt b/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorViewModel.kt
index c99766e8..ce841bac 100644
--- a/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorViewModel.kt
+++ b/feature/goal-editor/src/main/java/com/twix/goal_editor/GoalEditorViewModel.kt
@@ -4,7 +4,7 @@ import androidx.lifecycle.viewModelScope
import com.twix.designsystem.R
import com.twix.designsystem.components.toast.model.ToastType
import com.twix.domain.model.enums.GoalIconType
-import com.twix.domain.model.enums.RepeatType
+import com.twix.domain.model.enums.RepeatCycle
import com.twix.goal_editor.model.GoalEditorUiState
import com.twix.ui.base.BaseViewModel
import kotlinx.coroutines.launch
@@ -20,7 +20,7 @@ class GoalEditorViewModel :
is GoalEditorIntent.SetIcon -> setIcon(intent.icon)
is GoalEditorIntent.SetEndDate -> setEndDate(intent.endDate)
is GoalEditorIntent.SetRepeatCount -> setRepeatCount(intent.repeatCount)
- is GoalEditorIntent.SetRepeatType -> setRepeatType(intent.repeatType)
+ is GoalEditorIntent.SetRepeatType -> setRepeatType(intent.repeatCycle)
is GoalEditorIntent.SetStartDate -> setStartDate(intent.startDate)
is GoalEditorIntent.SetTitle -> setTitle(intent.title)
is GoalEditorIntent.SetEndDateEnabled -> setEndDateEnabled(intent.enabled)
@@ -37,8 +37,8 @@ class GoalEditorViewModel :
reduce { copy(goalTitle = title) }
}
- private fun setRepeatType(repeatType: RepeatType) {
- reduce { copy(selectedRepeatType = repeatType) }
+ private fun setRepeatType(repeatCycle: RepeatCycle) {
+ reduce { copy(selectedRepeatCycle = repeatCycle) }
}
private fun setRepeatCount(repeatCount: Int) {
diff --git a/feature/goal-editor/src/main/java/com/twix/goal_editor/component/EmojiPicker.kt b/feature/goal-editor/src/main/java/com/twix/goal_editor/component/EmojiPicker.kt
index d3aa81c8..c0640244 100644
--- a/feature/goal-editor/src/main/java/com/twix/goal_editor/component/EmojiPicker.kt
+++ b/feature/goal-editor/src/main/java/com/twix/goal_editor/component/EmojiPicker.kt
@@ -12,7 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
-import com.twix.designsystem.R
+import com.twix.designsystem.extension.toRes
import com.twix.designsystem.theme.GrayColor
import com.twix.domain.model.enums.GoalIconType
import com.twix.ui.extension.noRippleClickable
@@ -40,16 +40,3 @@ fun EmojiPicker(
)
}
}
-
-@Composable
-fun GoalIconType.toRes(): Int =
- when (this) {
- GoalIconType.DEFAULT -> R.drawable.ic_default
- GoalIconType.CLEAN -> R.drawable.ic_clean
- GoalIconType.EXERCISE -> R.drawable.ic_exercise
- GoalIconType.BOOK -> R.drawable.ic_book
- GoalIconType.PENCIL -> R.drawable.ic_pencil
- GoalIconType.HEALTH -> R.drawable.ic_health
- GoalIconType.HEART -> R.drawable.ic_heart
- GoalIconType.LAPTOP -> R.drawable.ic_laptop
- }
diff --git a/feature/goal-editor/src/main/java/com/twix/goal_editor/component/GoalInfoCard.kt b/feature/goal-editor/src/main/java/com/twix/goal_editor/component/GoalInfoCard.kt
index c6d95d47..f5b739fc 100644
--- a/feature/goal-editor/src/main/java/com/twix/goal_editor/component/GoalInfoCard.kt
+++ b/feature/goal-editor/src/main/java/com/twix/goal_editor/component/GoalInfoCard.kt
@@ -33,18 +33,18 @@ import com.twix.designsystem.components.text.AppText
import com.twix.designsystem.theme.CommonColor
import com.twix.designsystem.theme.GrayColor
import com.twix.domain.model.enums.AppTextStyle
-import com.twix.domain.model.enums.RepeatType
+import com.twix.domain.model.enums.RepeatCycle
import com.twix.ui.extension.noRippleClickable
import java.time.LocalDate
@Composable
fun GoalInfoCard(
- selectedRepeatType: RepeatType,
+ selectedRepeatCycle: RepeatCycle,
repeatCount: Int,
startDate: LocalDate,
endDateEnabled: Boolean,
endDate: LocalDate,
- onSelectedRepeatType: (RepeatType) -> Unit,
+ onSelectedRepeatType: (RepeatCycle) -> Unit,
onShowRepeatCountBottomSheet: () -> Unit,
onShowCalendarBottomSheet: (Boolean) -> Unit, // true면 endDate
onToggleEndDateEnabled: (Boolean) -> Unit,
@@ -57,7 +57,7 @@ fun GoalInfoCard(
.border(1.dp, GrayColor.C500, RoundedCornerShape(12.dp)),
) {
RepeatTypeSettings(
- selectedRepeatType = selectedRepeatType,
+ selectedRepeatCycle = selectedRepeatCycle,
repeatCount = repeatCount,
onSelectedRepeatType = onSelectedRepeatType,
onShowRepeatCountBottomSheet = onShowRepeatCountBottomSheet,
@@ -97,9 +97,9 @@ fun GoalInfoCard(
@Composable
private fun RepeatTypeSettings(
- selectedRepeatType: RepeatType,
+ selectedRepeatCycle: RepeatCycle,
repeatCount: Int,
- onSelectedRepeatType: (RepeatType) -> Unit,
+ onSelectedRepeatType: (RepeatCycle) -> Unit,
onShowRepeatCountBottomSheet: () -> Unit,
) {
val animationDuration = 160
@@ -119,8 +119,8 @@ private fun RepeatTypeSettings(
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
- RepeatType.entries.forEachIndexed { index, type ->
- val isSelected = selectedRepeatType == type
+ RepeatCycle.entries.forEachIndexed { index, type ->
+ val isSelected = selectedRepeatCycle == type
AppText(
text = type.label(),
@@ -135,13 +135,13 @@ private fun RepeatTypeSettings(
.noRippleClickable(onClick = { onSelectedRepeatType(type) }),
)
- if (index != RepeatType.entries.lastIndex) Spacer(Modifier.width(8.dp))
+ if (index != RepeatCycle.entries.lastIndex) Spacer(Modifier.width(8.dp))
}
Spacer(Modifier.weight(1f))
AnimatedVisibility(
- visible = selectedRepeatType != RepeatType.DAILY,
+ visible = selectedRepeatCycle != RepeatCycle.DAILY,
enter = fadeIn(animationSpec = tween(durationMillis = animationDuration)),
exit = fadeOut(animationSpec = tween(durationMillis = animationDuration)),
) {
@@ -153,7 +153,7 @@ private fun RepeatTypeSettings(
verticalAlignment = Alignment.CenterVertically,
) {
AppText(
- text = stringResource(R.string.repeat_count, selectedRepeatType.label(), repeatCount),
+ text = stringResource(R.string.repeat_count, selectedRepeatCycle.label(), repeatCount),
style = AppTextStyle.B2,
color = GrayColor.C500,
)
@@ -245,9 +245,9 @@ private fun HeaderText(text: String) {
}
@Composable
-fun RepeatType.label(): String =
+fun RepeatCycle.label(): String =
when (this) {
- RepeatType.DAILY -> stringResource(R.string.word_daily)
- RepeatType.WEEKLY -> stringResource(R.string.word_weekly)
- RepeatType.MONTHLY -> stringResource(R.string.word_monthly)
+ RepeatCycle.DAILY -> stringResource(R.string.word_daily)
+ RepeatCycle.WEEKLY -> stringResource(R.string.word_weekly)
+ RepeatCycle.MONTHLY -> stringResource(R.string.word_monthly)
}
diff --git a/feature/goal-editor/src/main/java/com/twix/goal_editor/model/GoalEditorUiState.kt b/feature/goal-editor/src/main/java/com/twix/goal_editor/model/GoalEditorUiState.kt
index feee3572..7cc8ca7b 100644
--- a/feature/goal-editor/src/main/java/com/twix/goal_editor/model/GoalEditorUiState.kt
+++ b/feature/goal-editor/src/main/java/com/twix/goal_editor/model/GoalEditorUiState.kt
@@ -2,7 +2,7 @@ package com.twix.goal_editor.model
import androidx.compose.runtime.Immutable
import com.twix.domain.model.enums.GoalIconType
-import com.twix.domain.model.enums.RepeatType
+import com.twix.domain.model.enums.RepeatCycle
import com.twix.ui.base.State
import java.time.LocalDate
@@ -10,7 +10,7 @@ import java.time.LocalDate
data class GoalEditorUiState(
val selectedIcon: GoalIconType = GoalIconType.DEFAULT,
val goalTitle: String = "",
- val selectedRepeatType: RepeatType = RepeatType.DAILY,
+ val selectedRepeatCycle: RepeatCycle = RepeatCycle.DAILY,
val repeatCount: Int = 0,
val startDate: LocalDate = LocalDate.now(),
val endDateEnabled: Boolean = false,
diff --git a/feature/main/src/main/java/com/twix/home/HomeSideEffect.kt b/feature/main/src/main/java/com/twix/home/HomeSideEffect.kt
index 5b1046d5..5f5ee5a2 100644
--- a/feature/main/src/main/java/com/twix/home/HomeSideEffect.kt
+++ b/feature/main/src/main/java/com/twix/home/HomeSideEffect.kt
@@ -1,7 +1,14 @@
package com.twix.home
+import androidx.annotation.StringRes
+import com.twix.designsystem.components.toast.model.ToastType
import com.twix.ui.base.SideEffect
interface HomeSideEffect : SideEffect {
data object ShowMonthPickerBottomSheet : HomeSideEffect
+
+ data class ShowToast(
+ @param:StringRes val resId: Int,
+ val type: ToastType,
+ ) : HomeSideEffect
}
diff --git a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt
index ec9a25b7..66465446 100644
--- a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt
+++ b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt
@@ -1,8 +1,10 @@
package com.twix.home
import androidx.lifecycle.viewModelScope
-import com.twix.designsystem.components.toast.ToastManager
+import com.twix.designsystem.R
+import com.twix.designsystem.components.toast.model.ToastType
import com.twix.domain.model.enums.WeekNavigation
+import com.twix.domain.repository.GoalRepository
import com.twix.home.model.CalendarState
import com.twix.home.model.HomeUiState
import com.twix.ui.base.BaseViewModel
@@ -14,7 +16,7 @@ import kotlinx.coroutines.flow.stateIn
import java.time.LocalDate
class HomeViewModel(
- private val toastManager: ToastManager,
+ private val goalRepository: GoalRepository,
) : BaseViewModel(
HomeUiState(),
) {
@@ -38,6 +40,10 @@ class HomeViewModel(
),
)
+ init {
+ fetchGoalList()
+ }
+
override suspend fun handleIntent(intent: HomeIntent) {
when (intent) {
is HomeIntent.SelectDate -> updateDate(intent.date)
@@ -54,6 +60,8 @@ class HomeViewModel(
if (date.month != currentState.visibleDate.month) updateVisibleDate(date)
reduce { copy(selectedDate = date, referenceDate = date) }
+
+ fetchGoalList()
}
private fun shiftWeek(action: WeekNavigation) {
@@ -64,7 +72,10 @@ class HomeViewModel(
WeekNavigation.TODAY -> LocalDate.now()
}
if (currentState.referenceDate == newReference) return
- if (action == WeekNavigation.TODAY) updateDate(newReference)
+ if (action == WeekNavigation.TODAY) {
+ updateDate(newReference)
+ return
+ }
reduce { copy(referenceDate = newReference) }
}
@@ -73,4 +84,19 @@ class HomeViewModel(
reduce { copy(visibleDate = date) }
}
+
+ /**
+ * 서버에서 데이터를 가져오는 부분
+ * */
+ private fun fetchGoalList() {
+ val date = currentState.selectedDate.toString()
+
+ launchResult(
+ block = { goalRepository.fetchGoalList(date = date) },
+ onSuccess = { goalList -> reduce { copy(goalList = goalList) } },
+ onError = {
+ emitSideEffect(HomeSideEffect.ShowToast(R.string.toast_goal_fetch_failed, ToastType.ERROR))
+ },
+ )
+ }
}
diff --git a/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt b/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt
index 0eab724c..a411ad27 100644
--- a/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt
+++ b/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt
@@ -1,6 +1,7 @@
package com.twix.home.model
import androidx.compose.runtime.Immutable
+import com.twix.domain.model.goal.GoalList
import com.twix.ui.base.State
import java.time.LocalDate
import java.time.YearMonth
@@ -11,6 +12,7 @@ data class HomeUiState(
val visibleDate: LocalDate = LocalDate.now(), // 홈 화면 상단에 존재하는 월, 년 텍스트를 위한 상태 변수
val selectedDate: LocalDate = LocalDate.now(),
val referenceDate: LocalDate = LocalDate.now(), // 7일 달력을 생성하기 위한 레퍼런스 날짜
+ val goalList: GoalList = GoalList(),
) : State {
val monthYear: String
get() = "${visibleDate.month.value}월 ${visibleDate.year}"