Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ interface AuthRemoteDataSource {
suspend fun login(socialAccessToken: String, loginRequestDto: LoginRequestDto): Result<LoginResponseDto>
suspend fun submitAgreement(termsAgreementRequestDto: TermsAgreementRequestDto): Result<Unit>
suspend fun logout(): Result<Unit>
suspend fun withdrawal(): Result<Unit>
suspend fun withdrawal(reason: String): Result<Unit>
suspend fun reissueToken(refreshToken: String): Result<LoginResponseDto>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.threegap.bitnagil.data.auth.datasourceimpl
import com.threegap.bitnagil.data.auth.datasource.AuthRemoteDataSource
import com.threegap.bitnagil.data.auth.model.request.LoginRequestDto
import com.threegap.bitnagil.data.auth.model.request.TermsAgreementRequestDto
import com.threegap.bitnagil.data.auth.model.request.WithdrawalReasonRequest
import com.threegap.bitnagil.data.auth.model.response.LoginResponseDto
import com.threegap.bitnagil.data.auth.service.AuthService
import com.threegap.bitnagil.data.common.safeApiCall
Expand All @@ -27,9 +28,9 @@ class AuthRemoteDataSourceImpl @Inject constructor(
authService.postLogout()
}

override suspend fun withdrawal(): Result<Unit> =
override suspend fun withdrawal(reason: String): Result<Unit> =
safeUnitApiCall {
authService.postWithdrawal()
authService.postWithdrawal(WithdrawalReasonRequest(reason))
}

override suspend fun reissueToken(refreshToken: String): Result<LoginResponseDto> =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.threegap.bitnagil.data.auth.model.request

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class WithdrawalReasonRequest(
@SerialName("reasonOfWithdrawal") val reasonOfWithdrawal: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class AuthRepositoryImpl @Inject constructor(
}
}

override suspend fun withdrawal(): Result<Unit> {
return authRemoteDataSource.withdrawal().also {
override suspend fun withdrawal(reason: String): Result<Unit> {
return authRemoteDataSource.withdrawal(reason).also {
if (it.isSuccess) authLocalDataSource.clearAuthToken()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.threegap.bitnagil.data.auth.service

import com.threegap.bitnagil.data.auth.model.request.LoginRequestDto
import com.threegap.bitnagil.data.auth.model.request.TermsAgreementRequestDto
import com.threegap.bitnagil.data.auth.model.request.WithdrawalReasonRequest
import com.threegap.bitnagil.data.auth.model.response.LoginResponseDto
import com.threegap.bitnagil.network.model.BaseResponse
import retrofit2.http.Body
Expand All @@ -23,7 +24,7 @@ interface AuthService {
): BaseResponse<Unit>

@POST("/api/v1/auth/withdrawal")
suspend fun postWithdrawal(): BaseResponse<Unit>
suspend fun postWithdrawal(@Body request: WithdrawalReasonRequest): BaseResponse<Unit>

@POST("/api/v1/auth/logout")
suspend fun postLogout(): BaseResponse<Unit>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface AuthRepository {
suspend fun login(socialAccessToken: String, socialType: String): Result<AuthSession>
suspend fun submitAgreement(termsAgreement: TermsAgreement): Result<Unit>
suspend fun logout(): Result<Unit>
suspend fun withdrawal(): Result<Unit>
suspend fun withdrawal(reason: String): Result<Unit>

suspend fun reissueToken(refreshToken: String): Result<AuthSession>
suspend fun getRefreshToken(): String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ import javax.inject.Inject
class WithdrawalUseCase @Inject constructor(
private val authRepository: AuthRepository,
) {
suspend operator fun invoke(): Result<Unit> = authRepository.withdrawal()
suspend operator fun invoke(reason: String): Result<Unit> = authRepository.withdrawal(reason)
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.threegap.bitnagil.designsystem.BitnagilTheme
import com.threegap.bitnagil.designsystem.R
import com.threegap.bitnagil.designsystem.component.atom.BitnagilIcon
Expand All @@ -40,22 +39,22 @@ import com.threegap.bitnagil.designsystem.component.atom.BitnagilSelectButtonCol
import com.threegap.bitnagil.designsystem.component.atom.BitnagilTextButton
import com.threegap.bitnagil.designsystem.component.block.BitnagilTopBar
import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple
import com.threegap.bitnagil.presentation.common.flow.collectAsEffect
import com.threegap.bitnagil.presentation.withdrawal.component.WithdrawalConfirmDialog
import com.threegap.bitnagil.presentation.withdrawal.model.WithdrawalIntent
import com.threegap.bitnagil.presentation.withdrawal.model.WithdrawalReason
import com.threegap.bitnagil.presentation.withdrawal.model.WithdrawalSideEffect
import com.threegap.bitnagil.presentation.withdrawal.model.WithdrawalState
import org.orbitmvi.orbit.compose.collectAsState
import org.orbitmvi.orbit.compose.collectSideEffect

@Composable
fun WithdrawalScreenContainer(
viewModel: WithdrawalViewModel = hiltViewModel(),
navigateToBack: () -> Unit,
navigateToLogin: () -> Unit,
viewModel: WithdrawalViewModel = hiltViewModel(),
) {
val uiState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
val uiState by viewModel.collectAsState()

viewModel.sideEffectFlow.collectAsEffect { sideEffect ->
viewModel.collectSideEffect { sideEffect ->
when (sideEffect) {
is WithdrawalSideEffect.NavigateToBack -> navigateToBack()
is WithdrawalSideEffect.NavigateToLogin -> navigateToLogin()
Expand All @@ -64,16 +63,16 @@ fun WithdrawalScreenContainer(

if (uiState.showSuccessDialog) {
WithdrawalConfirmDialog(
onConfirm = { viewModel.sendIntent(WithdrawalIntent.OnSuccessDialogConfirm) },
onConfirm = viewModel::navigateToLogin,
)
}

WithdrawalScreen(
uiState = uiState,
onTermsToggle = { viewModel.sendIntent(WithdrawalIntent.OnTermsToggle) },
onReasonSelect = { viewModel.sendIntent(WithdrawalIntent.OnReasonSelected(it)) },
onCustomReasonChanged = { viewModel.sendIntent(WithdrawalIntent.OnCustomReasonChanged(it)) },
onBackClick = { viewModel.sendIntent(WithdrawalIntent.OnBackClick) },
onTermsToggle = viewModel::onTermsToggle,
onReasonSelect = viewModel::updateSelectedReason,
onCustomReasonChanged = viewModel::updateCustomReason,
onBackClick = viewModel::navigateToBack,
onWithdrawalClick = viewModel::withdrawal,
)
}
Expand All @@ -94,7 +93,6 @@ private fun WithdrawalScreen(
Column(
modifier = Modifier
.fillMaxSize()
.background(BitnagilTheme.colors.white)
.statusBarsPadding()
.windowInsetsPadding(WindowInsets.ime),
) {
Expand Down Expand Up @@ -221,13 +219,11 @@ private fun WithdrawalScreen(
}
}

@Preview
@Preview(showBackground = true)
@Composable
private fun WithdrawalScreenPreview() {
WithdrawalScreen(
uiState = WithdrawalState(
isTermsChecked = true,
),
uiState = WithdrawalState.INIT,
onTermsToggle = {},
onReasonSelect = {},
onCustomReasonChanged = {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,72 +1,66 @@
package com.threegap.bitnagil.presentation.withdrawal

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.ViewModel
import com.threegap.bitnagil.domain.auth.usecase.WithdrawalUseCase
import com.threegap.bitnagil.presentation.common.mviviewmodel.MviViewModel
import com.threegap.bitnagil.presentation.withdrawal.model.WithdrawalIntent
import com.threegap.bitnagil.presentation.withdrawal.model.WithdrawalReason
import com.threegap.bitnagil.presentation.withdrawal.model.WithdrawalSideEffect
import com.threegap.bitnagil.presentation.withdrawal.model.WithdrawalState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import org.orbitmvi.orbit.syntax.Syntax
import org.orbitmvi.orbit.Container
import org.orbitmvi.orbit.ContainerHost
import org.orbitmvi.orbit.viewmodel.container
import javax.inject.Inject

@HiltViewModel
class WithdrawalViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val withdrawalUseCase: WithdrawalUseCase,
) : MviViewModel<WithdrawalState, WithdrawalSideEffect, WithdrawalIntent>(
savedStateHandle = savedStateHandle,
initState = WithdrawalState(),
) {
override suspend fun Syntax<WithdrawalState, WithdrawalSideEffect>.reduceState(
intent: WithdrawalIntent,
state: WithdrawalState,
): WithdrawalState? {
val newState = when (intent) {
is WithdrawalIntent.UpdateLoading -> state.copy(isLoading = intent.isLoading)
is WithdrawalIntent.OnTermsToggle -> state.copy(isTermsChecked = !state.isTermsChecked)
is WithdrawalIntent.ShowSuccessDialog -> state.copy(showSuccessDialog = true)
) : ContainerHost<WithdrawalState, WithdrawalSideEffect>, ViewModel() {

is WithdrawalIntent.OnCustomReasonChanged -> {
state.copy(customReasonText = intent.text)
}
override val container: Container<WithdrawalState, WithdrawalSideEffect> = container(initialState = WithdrawalState.INIT)

is WithdrawalIntent.OnReasonSelected -> {
state.copy(
selectedReason = intent.reason,
customReasonText = "",
)
}

is WithdrawalIntent.OnBackClick -> {
sendSideEffect(WithdrawalSideEffect.NavigateToBack)
null
}
fun onTermsToggle() {
intent {
reduce { state.copy(isTermsChecked = !state.isTermsChecked) }
}
}

is WithdrawalIntent.OnSuccessDialogConfirm -> {
sendSideEffect(WithdrawalSideEffect.NavigateToLogin)
null
}
fun updateCustomReason(text: String) {
intent {
reduce { state.copy(customReasonText = text) }
}
}

return newState
fun updateSelectedReason(reason: WithdrawalReason?) {
intent {
reduce { state.copy(selectedReason = reason, customReasonText = "") }
}
}

fun withdrawal() {
if (container.stateFlow.value.isLoading) return
sendIntent(WithdrawalIntent.UpdateLoading(true))
viewModelScope.launch {
withdrawalUseCase().fold(
intent {
if (state.isLoading) return@intent
reduce { state.copy(isLoading = true) }
val reason = state.finalWithdrawalReason
withdrawalUseCase(reason).fold(
onSuccess = {
sendIntent(WithdrawalIntent.UpdateLoading(false))
sendIntent(WithdrawalIntent.ShowSuccessDialog)
reduce { state.copy(isLoading = false, showSuccessDialog = true) }
},
onFailure = {
sendIntent(WithdrawalIntent.UpdateLoading(false))
reduce { state.copy(isLoading = false) }
},
)
}
}

fun navigateToBack() {
intent {
postSideEffect(WithdrawalSideEffect.NavigateToBack)
}
}

fun navigateToLogin() {
intent {
postSideEffect(WithdrawalSideEffect.NavigateToLogin)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.threegap.bitnagil.presentation.withdrawal.model

import com.threegap.bitnagil.presentation.common.mviviewmodel.MviSideEffect

sealed interface WithdrawalSideEffect : MviSideEffect {
sealed interface WithdrawalSideEffect {
data object NavigateToBack : WithdrawalSideEffect
data object NavigateToLogin : WithdrawalSideEffect
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package com.threegap.bitnagil.presentation.withdrawal.model

import com.threegap.bitnagil.presentation.common.mviviewmodel.MviState
import kotlinx.parcelize.Parcelize

@Parcelize
data class WithdrawalState(
val isLoading: Boolean = false,
val isTermsChecked: Boolean = false,
val selectedReason: WithdrawalReason? = null,
val customReasonText: String = "",
val showSuccessDialog: Boolean = false,
) : MviState {
val isLoading: Boolean,
val isTermsChecked: Boolean,
val selectedReason: WithdrawalReason?,
val customReasonText: String,
val showSuccessDialog: Boolean,
) {
val isWithdrawalEnabled: Boolean
get() = isTermsChecked && (selectedReason != null || customReasonText.isNotBlank())

val finalWithdrawalReason: String
get() = selectedReason?.displayText ?: customReasonText

companion object {
val INIT = WithdrawalState(
isLoading = false,
isTermsChecked = false,
selectedReason = null,
customReasonText = "",
showSuccessDialog = false,
)
}
}