-
Notifications
You must be signed in to change notification settings - Fork 1
목표 생성 API 연동 #71
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
목표 생성 API 연동 #71
Changes from all commits
62ab30f
c3b0ca1
baa20ae
2910aeb
8943825
87f2495
06ea3cc
ec73e2e
2dd4a5d
32ab8d2
d8063ef
965d4a9
866326a
d181776
536514f
26b8450
481e9a6
f552e5f
6c133f6
131448f
3cf16d6
9bf2f1f
81ec510
84715e3
56be48f
3a53728
33af4ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.twix.network.model.request.goal.mapper | ||
|
|
||
| import com.twix.domain.model.goal.CreateGoalParam | ||
| import com.twix.network.model.request.goal.model.CreateGoalRequest | ||
|
|
||
| fun CreateGoalParam.toRequest(): CreateGoalRequest = | ||
| CreateGoalRequest( | ||
| name = name, | ||
| icon = icon.toApi(), | ||
| repeatCycle = repeatCycle.toApi(), | ||
| repeatCount = repeatCount, | ||
| startDate = startDate.toString(), | ||
| endDate = endDate?.toString(), | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.twix.network.model.request.goal.model | ||
|
|
||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class CreateGoalRequest( | ||
| @SerialName("name") val name: String, | ||
| @SerialName("icon") val icon: String, | ||
| @SerialName("repeatCycle") val repeatCycle: String, | ||
| @SerialName("repeatCount") val repeatCount: Int, | ||
| @SerialName("startDate") val startDate: String, | ||
| @SerialName("endDate") val endDate: String?, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.twix.network.model.response.goal.model | ||
|
|
||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class CreateGoalResponse( | ||
| @SerialName("goalId") val goalId: Long, | ||
| @SerialName("name") val name: String, | ||
| @SerialName("icon") val icon: String, | ||
| @SerialName("repeatCycle") val repeatCycle: String, | ||
| @SerialName("repeatCount") val repeatCount: Int, | ||
| @SerialName("startDate") val startDate: String, | ||
| @SerialName("endDate") val endDate: String?, | ||
| @SerialName("goalStatus") val goalStatus: String, | ||
| @SerialName("createdAt") val createdAt: String, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,21 @@ | ||
| package com.twix.network.service | ||
|
|
||
| import com.twix.network.model.request.goal.model.CreateGoalRequest | ||
| import com.twix.network.model.response.goal.model.CreateGoalResponse | ||
| import com.twix.network.model.response.goal.model.GoalListResponse | ||
| import de.jensklingenberg.ktorfit.http.Body | ||
| import de.jensklingenberg.ktorfit.http.GET | ||
| import de.jensklingenberg.ktorfit.http.POST | ||
| import de.jensklingenberg.ktorfit.http.Query | ||
|
|
||
| interface GoalService { | ||
| @GET("api/v1/goals") | ||
| suspend fun fetchGoals( | ||
| @Query("date") date: String, | ||
| ): GoalListResponse | ||
|
|
||
| @POST("api/v1/goals") | ||
| suspend fun createGoal( | ||
| @Body body: CreateGoalRequest, | ||
| ): CreateGoalResponse | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,4 +10,8 @@ class SideEffectHolder<S : SideEffect> { | |
| suspend fun emit(effect: S) { | ||
| channel.send(effect) | ||
| } | ||
|
|
||
| fun tryEmit(effect: S) { | ||
| channel.trySend(effect) | ||
| } | ||
|
Comment on lines
+14
to
+16
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 토끼 리뷰 참고해서 send와 trySend 어떤걸 사용할지 이야기 해보면 좋을 것 같아 ! 나도 요거에 대해서 배포 끝나면 공부해볼태니 이야기 해보는거 어때 ? |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| plugins { | ||
| alias(libs.plugins.twix.android.library) | ||
| alias(libs.plugins.twix.koin) | ||
| } | ||
|
|
||
| android { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.twix.util.bus | ||
|
|
||
| import kotlinx.coroutines.flow.MutableSharedFlow | ||
| import kotlinx.coroutines.flow.SharedFlow | ||
|
|
||
| class GoalRefreshBus { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 클래스의 역할이 어떤건지 설명해줄 수 있을ㄲ ㅏ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HomeScreen으로 진입할 때 LaunchedEffect로 매번 API를 부르면 비효율적이니까 다른 NavGraph에서 데이터가 생성되거나 삭제됐음을 알리는 용도입니다! 딱 필요할 때만 데이터를 불러오도록 돕는 역할이에요 |
||
| private val _events = | ||
| MutableSharedFlow<Unit>( | ||
| replay = 0, | ||
| extraBufferCapacity = 1, | ||
| ) | ||
| val events: SharedFlow<Unit> = _events | ||
|
|
||
| fun notifyChanged() = _events.tryEmit(Unit) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.twix.util.di | ||
|
|
||
| import com.twix.util.bus.GoalRefreshBus | ||
| import org.koin.dsl.module | ||
|
|
||
| val utilModule = | ||
| module { | ||
| single { GoalRefreshBus() } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.twix.domain.model.goal | ||
|
|
||
| import com.twix.domain.model.enums.GoalIconType | ||
| import com.twix.domain.model.enums.RepeatCycle | ||
| import java.time.LocalDate | ||
|
|
||
| data class CreateGoalParam( | ||
| val name: String, | ||
| val icon: GoalIconType, | ||
| val repeatCycle: RepeatCycle, | ||
| val repeatCount: Int, | ||
| val startDate: LocalDate, | ||
| val endDate: LocalDate?, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.twix.domain.model.goal | ||
|
|
||
| import com.twix.domain.model.enums.GoalIconType | ||
| import com.twix.domain.model.enums.RepeatCycle | ||
| import java.time.LocalDate | ||
|
|
||
| data class CreatedGoal( | ||
| val goalId: Long, | ||
| val name: String, | ||
| val icon: GoalIconType, | ||
| val repeatCycle: RepeatCycle, | ||
| val repeatCount: Int, | ||
| val startDate: LocalDate, | ||
| val endDate: LocalDate?, | ||
| val createdAt: String, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,12 @@ | ||
| package com.twix.domain.repository | ||
|
|
||
| import com.twix.domain.model.goal.CreateGoalParam | ||
| import com.twix.domain.model.goal.CreatedGoal | ||
| import com.twix.domain.model.goal.GoalList | ||
| import com.twix.result.AppResult | ||
|
|
||
| interface GoalRepository { | ||
| suspend fun fetchGoalList(date: String): AppResult<GoalList> | ||
|
|
||
| suspend fun createGoal(param: CreateGoalParam): AppResult<CreatedGoal> | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,15 +6,20 @@ import com.twix.designsystem.components.toast.model.ToastType | |
| import com.twix.domain.model.enums.GoalIconType | ||
| import com.twix.domain.model.enums.GoalReactionType | ||
| import com.twix.domain.model.enums.RepeatCycle | ||
| import com.twix.domain.model.goal.CreateGoalParam | ||
| import com.twix.domain.model.goal.Goal | ||
| import com.twix.domain.model.goal.GoalVerification | ||
| import com.twix.domain.repository.GoalRepository | ||
| import com.twix.goal_editor.model.GoalEditorUiState | ||
| import com.twix.ui.base.BaseViewModel | ||
| import com.twix.util.bus.GoalRefreshBus | ||
| import kotlinx.coroutines.launch | ||
| import java.time.LocalDate | ||
|
|
||
| class GoalEditorViewModel : | ||
| BaseViewModel<GoalEditorUiState, GoalEditorIntent, GoalEditorSideEffect>( | ||
| class GoalEditorViewModel( | ||
| private val goalRepository: GoalRepository, | ||
| private val goalRefreshBus: GoalRefreshBus, | ||
| ) : BaseViewModel<GoalEditorUiState, GoalEditorIntent, GoalEditorSideEffect>( | ||
| GoalEditorUiState(), | ||
| ) { | ||
| override suspend fun handleIntent(intent: GoalEditorIntent) { | ||
|
|
@@ -42,7 +47,7 @@ class GoalEditorViewModel : | |
| } | ||
|
|
||
| private fun setRepeatType(repeatCycle: RepeatCycle) { | ||
| reduce { copy(selectedRepeatCycle = repeatCycle) } | ||
| reduce { copy(selectedRepeatCycle = repeatCycle, repeatCount = 1) } | ||
| } | ||
|
|
||
| private fun setRepeatCount(repeatCount: Int) { | ||
|
|
@@ -72,6 +77,15 @@ class GoalEditorViewModel : | |
| } | ||
| return | ||
| } | ||
|
|
||
| launchResult( | ||
| block = { goalRepository.createGoal(currentState.toCreateParam()) }, | ||
| onSuccess = { | ||
| goalRefreshBus.notifyChanged() | ||
| tryEmitSideEffect(GoalEditorSideEffect.NavigateToHome) | ||
| }, | ||
| onError = { emitSideEffect(GoalEditorSideEffect.ShowToast(R.string.toast_create_goal_failed, ToastType.ERROR)) }, | ||
| ) | ||
|
Comment on lines
+80
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Find the GoalEditorViewModel file and search for sideEffectHolder
find . -name "GoalEditorViewModel.kt" -type fRepository: YAPP-Github/Twix-Android Length of output: 147 🏁 Script executed: # Search for MutableSharedFlow configuration in the codebase
rg "MutableSharedFlow|sideEffectHolder" --type kotlin -B 2 -A 2Repository: YAPP-Github/Twix-Android Length of output: 3386 🏁 Script executed: # Check for ViewModel base class that might define sideEffectHolder
fd -e kt -path "*/ViewModel.kt" | head -20Repository: YAPP-Github/Twix-Android Length of output: 236 🏁 Script executed: # Find SideEffectHolder implementation
rg "class SideEffectHolder" --type kotlin -A 10Repository: YAPP-Github/Twix-Android Length of output: 989 🏁 Script executed: # Also check the BaseViewModel for more context
cat core/ui/src/main/java/com/twix/ui/base/BaseViewModel.ktRepository: YAPP-Github/Twix-Android Length of output: 4374
네비게이션처럼 반드시 전달되어야 하는 반면 개선 방안: 개선 예시launchResult(
block = { goalRepository.createGoal(currentState.toCreateParam()) },
onSuccess = {
goalRefreshBus.notifyChanged()
viewModelScope.launch {
emitSideEffect(GoalEditorSideEffect.NavigateToHome)
}
},
onError = { emitSideEffect(GoalEditorSideEffect.ShowToast(R.string.toast_create_goal_failed, ToastType.ERROR)) },
)
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| private fun initGoal(id: Long) { | ||
|
|
@@ -113,4 +127,14 @@ class GoalEditorViewModel : | |
| ) | ||
| } | ||
| } | ||
|
|
||
| private fun GoalEditorUiState.toCreateParam(): CreateGoalParam = | ||
| CreateGoalParam( | ||
| name = goalTitle.trim(), | ||
| icon = selectedIcon, | ||
| repeatCycle = selectedRepeatCycle, | ||
| repeatCount = repeatCount, | ||
| startDate = startDate, | ||
| endDate = if (endDateEnabled) endDate else null, | ||
| ) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
서버도 코틀린을 사용하다보니 카멜 케이스를 사용하는게 똑같아서
파라미터 네이밍이 거의 같을 것 같은데 SerialName을 사용하는 것에 대해서 컨벤션을 정해보면 좋을 것 같아 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
나중에 서버에서 변수명 변경할 수도 있어서 확장성 고려해서 다 붙여놨어요!