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

[ Feature ] : QR 체크 -> 암호 입력 변경 #319

Merged
merged 8 commits into from
Oct 27, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ interface FirebaseRemoteConfigDataSource {
suspend fun shouldShowGuestButton(): Boolean
suspend fun getVersionInfo(): VersionEntity
suspend fun getSignUpPassword(): String
suspend fun getSessionPassword(): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class FirebaseRemoteConfigDataSourceImpl @Inject constructor() : FirebaseRemoteC
}
}
}

override suspend fun getSignUpPassword(): String {
return suspendCancellableCoroutine { cancellableContinuation ->
firebaseRemoteConfig.fetchAndActivate().addOnSuccessListener {
Expand All @@ -133,4 +134,17 @@ class FirebaseRemoteConfigDataSourceImpl @Inject constructor() : FirebaseRemoteC
}
}
}

override suspend fun getSessionPassword(): String {
return suspendCancellableCoroutine { cancellableContinuation ->
firebaseRemoteConfig.fetchAndActivate().addOnSuccessListener {
val sessionPassword = firebaseRemoteConfig.getString(RemoteConfigData.SessionPassword.key)
cancellableContinuation.resume(sessionPassword, null)
}.addOnFailureListener { exception ->
cancellableContinuation.resumeWithException(exception)
}
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,18 @@ class RemoteConfigRepositoryImpl @Inject constructor(
}
)
}

override suspend fun getSessionPassword(): Result<String> {
return runCatching {
firebaseRemoteConfigDataSource.getSessionPassword()
}.fold(
onSuccess = { sessionPassword: String ->
Result.success(sessionPassword)
},
onFailure = { exception ->
Result.failure(exception)
}
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ sealed class RemoteConfigData<T> {
override val defaultValue: String = ""
}

object SessionPassword : RemoteConfigData<String>() {
override val key: String = SESSION_PASSWORD
override val defaultValue: String = ""
}

companion object {
private const val ATTENDANCE_MAGINOTLINE_TIME = "attendance_maginotline_time"
private const val ATTENDANCE_SESSION_LIST = "attendance_session_list"
Expand All @@ -53,6 +58,7 @@ sealed class RemoteConfigData<T> {
private const val SHOULD_SHOW_GUEST_BUTTON = "should_show_guest_button"
private const val ATTENDANCE_VERSION_INFO = "attendance_version"
private const val SIGN_UP_PASSWORD = "attendance_signup_password"
private const val SESSION_PASSWORD = "attendance_session_password"

val defaultMaps = mapOf(
MaginotlineTime.defaultValue to MaginotlineTime.key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ interface RemoteConfigRepository {
suspend fun shouldShowGuestButton(): Result<Boolean>
suspend fun getVersionInfo(): Result<Version>
suspend fun getSignUpPassword(): Result<String>
suspend fun getSessionPassword(): Result<String>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.yapp.domain.usecases

import com.yapp.domain.repository.RemoteConfigRepository
import javax.inject.Inject

class CheckSessionPasswordUseCase @Inject constructor(
private val remoteConfigRepository: RemoteConfigRepository,
) {
suspend operator fun invoke(inputPassword: String): Result<Boolean> {
return remoteConfigRepository.getSessionPassword().mapCatching { sessionPassword: String ->
inputPassword == sessionPassword
}
}
}
16 changes: 15 additions & 1 deletion domain/src/main/java/com/yapp/domain/util/DateUtil.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.yapp.domain.util

import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.Locale

object DateUtil {
Expand Down Expand Up @@ -34,4 +36,16 @@ object DateUtil {
fun isPastSession(sessionDateStr: String): Boolean {
return getElapsedTime(sessionDateStr) >= 0
}
}

fun parseDate(date: String, format: String): String {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")

if (date.isEmpty()) {
val now = LocalDateTime.now()
return formatter.format(now)
}

val currentDate = formatter.parse(date)
return DateTimeFormatter.ofPattern(format).format(currentDate)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import com.yapp.presentation.ui.member.score.detail.SessionDetail
import com.yapp.presentation.ui.member.setting.MemberSetting
import com.yapp.presentation.ui.member.signup.name.Name
import com.yapp.presentation.ui.member.signup.password.Password
import com.yapp.presentation.ui.member.signup.password.PasswordType
import com.yapp.presentation.ui.member.signup.position.Position
import com.yapp.presentation.ui.member.signup.team.Team
import com.yapp.presentation.ui.splash.Splash
Expand All @@ -66,15 +67,18 @@ fun AttendanceScreen(
LaunchedEffect(key1 = viewModel.effect) {
viewModel.effect.collect { effect ->
when (effect) {
is MainUiSideEffect.NavigateToQRScreen -> {
navController.navigate(BottomNavigationItem.QR_AUTH.route)
is MainUiSideEffect.NavigateToPassword -> {
navController.navigate(BottomNavigationItem.PASSWORD.route)
}

is MainUiSideEffect.ShowToast -> {
qrToastVisible = !qrToastVisible
delay(1000L)
qrToastVisible = !qrToastVisible
}
is MainUiSideEffect.NavigateToBack -> {
navController.popBackStack()
}
}
}
}
Expand Down Expand Up @@ -158,7 +162,7 @@ fun AttendanceScreen(
SetStatusBarColorByRoute(it.destination.route)
MemberMain(
navigateToScreen = { route ->
if (route == AttendanceScreenRoute.QR_AUTH.route) {
if (route == AttendanceScreenRoute.PASSWORD.route) {
viewModel.setEvent(MainUiEvent.OnClickQrAuthButton)
} else {
navController.navigate(route)
Expand All @@ -171,12 +175,14 @@ fun AttendanceScreen(
}

composable(
route = AttendanceScreenRoute.QR_AUTH.route
route = AttendanceScreenRoute.PASSWORD.route
) {
SetStatusBarColorByRoute(it.destination.route)
QrCodeScanner {
navController.popBackStack()
}
Password(
type = PasswordType.Session,
onClickBackButton = { navController.popBackStack() },
onClickNextButton = { viewModel.setEvent(MainUiEvent.OnValidatePassword) }
)
}

composable(
Expand Down Expand Up @@ -253,6 +259,7 @@ fun AttendanceScreen(
) {
SetStatusBarColorByRoute(it.destination.route)
Password(
type = PasswordType.SignUp,
onClickBackButton = {
navController.navigate(AttendanceScreenRoute.LOGIN.route) {
popUpTo(AttendanceScreenRoute.SIGNUP_PASSWORD.route) { inclusive = true }
Expand All @@ -262,7 +269,8 @@ fun AttendanceScreen(
navController.navigate(AttendanceScreenRoute.SIGNUP_NAME.route) {
popUpTo(AttendanceScreenRoute.SIGNUP_PASSWORD.route) { inclusive = true }
}
})
}
)
}

composable(
Expand Down Expand Up @@ -356,6 +364,7 @@ enum class AttendanceScreenRoute(val route: String) {
ADMIN_TOTAL_SCORE("admin-total-score"),
SESSION_DETAIL("session-detail"),
PRIVACY_POLICY("privacy-policy"),
PASSWORD("password"),
ADMIN_ATTENDANCE_MANAGEMENT("admin-attendance-management");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ class MainContract {

sealed class MainUiSideEffect : UiSideEffect {
object ShowToast : MainUiSideEffect()
object NavigateToQRScreen : MainUiSideEffect()
object NavigateToPassword : MainUiSideEffect()

object NavigateToBack : MainUiSideEffect()

}

sealed class MainUiEvent : UiEvent {
object OnClickQrAuthButton : MainUiEvent()
object OnValidatePassword : MainUiEvent()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.yapp.presentation.ui

import com.yapp.common.base.BaseViewModel
import com.yapp.domain.usecases.CheckQrAuthTimeUseCase
import com.yapp.domain.usecases.MarkAttendanceUseCase
import com.yapp.presentation.R
import com.yapp.presentation.common.AttendanceBundle
import com.yapp.presentation.ui.MainContract.MainUiEvent
Expand All @@ -13,14 +14,16 @@ import javax.inject.Inject
@HiltViewModel
class MainViewModel @Inject constructor(
private val resourcesProvider: ResourceProvider,
private val checkQrAuthTime: CheckQrAuthTimeUseCase
private val checkQrAuthTime: CheckQrAuthTimeUseCase,
private val markAttendanceUseCase: MarkAttendanceUseCase
) : BaseViewModel<MainContract.MainUiState, MainContract.MainUiSideEffect, MainUiEvent>(
MainContract.MainUiState()
) {

override suspend fun handleEvent(event: MainUiEvent) {
when (event) {
MainUiEvent.OnClickQrAuthButton -> checkAttendanceValidate()
MainUiEvent.OnValidatePassword -> onMarkAttendance()
}
}

Expand All @@ -29,7 +32,7 @@ class MainViewModel @Inject constructor(
checkQrAuthTime()
.onSuccess { isQRCheckEnable ->
if (isQRCheckEnable) {
setEffect(MainContract.MainUiSideEffect.NavigateToQRScreen)
setEffect(MainContract.MainUiSideEffect.NavigateToPassword)
} else {
showToast(resourcesProvider.getString(R.string.member_main_qr_enter_failed_toast_message))
}
Expand All @@ -42,8 +45,17 @@ class MainViewModel @Inject constructor(
}
}

private suspend fun onMarkAttendance() {
AttendanceBundle.upComingSession?.let {
markAttendanceUseCase(it)
.onSuccess { setEffect(MainContract.MainUiSideEffect.NavigateToBack) }
.onFailure { showToast(resourcesProvider.getString(R.string.member_qr_move_back_to_home_and_retry_error_message)) }
} ?: showToast(resourcesProvider.getString(R.string.member_qr_move_back_to_home_and_retry_error_message))
}


private fun showToast(text: String) {
setState { copy(toastMessage = text) }
setEffect(MainContract.MainUiSideEffect.ShowToast)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private fun navigateBottomNavigationScreen(
tab: BottomNavigationItem
) {
when (tab) {
BottomNavigationItem.QR_AUTH -> {
BottomNavigationItem.PASSWORD -> {
qrScreenNavigate.invoke(tab.route)
}
else -> {
Expand Down Expand Up @@ -153,7 +153,7 @@ fun BottomNavigationTab(
imageVector = ImageVector.vectorResource(id = navigationTab.icon),
contentDescription = null,
modifier = Modifier.padding(4.dp),
tint = if (navigationTab == BottomNavigationItem.QR_AUTH) Color.Unspecified else LocalContentColor.current,
tint = if (navigationTab == BottomNavigationItem.PASSWORD) Color.Unspecified else LocalContentColor.current,
)
},
label = if (navigationTab.title != null) {
Expand Down Expand Up @@ -184,7 +184,7 @@ enum class BottomNavigationItem(
R.drawable.icon_bottom_navigation_home,
R.string.member_main_bottom_navigation_today_session_text
),
QR_AUTH("qr-auth", R.drawable.icon_qr, null),
PASSWORD("password", R.drawable.icon_qr, null),
MEMBER_SCORE(
"member-score",
R.drawable.icon_bottom_navigation_check,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.yapp.presentation.ui.member.signup.password
import androidx.lifecycle.viewModelScope
import com.google.firebase.FirebaseNetworkException
import com.yapp.common.base.BaseViewModel
import com.yapp.domain.usecases.CheckSessionPasswordUseCase
import com.yapp.domain.usecases.CheckSignUpPasswordUseCase
import com.yapp.presentation.ui.member.signup.password.PasswordContract.PasswordSideEffect
import com.yapp.presentation.ui.member.signup.password.PasswordContract.PasswordUiEvent
Expand All @@ -15,6 +16,7 @@ import javax.inject.Inject
@HiltViewModel
internal class PassWordViewModel @Inject constructor(
private val checkSignUpPasswordUseCase: CheckSignUpPasswordUseCase,
private val checkSessionPasswordUseCase: CheckSessionPasswordUseCase
) : BaseViewModel<PasswordUiState, PasswordSideEffect, PasswordUiEvent>(PasswordUiState()) {

override suspend fun handleEvent(event: PasswordUiEvent) {
Expand All @@ -27,8 +29,11 @@ internal class PassWordViewModel @Inject constructor(
setEffect(PasswordSideEffect.NavigateToPreviousScreen)
}

PasswordUiEvent.OnNextButtonClick -> {
checkPassword(password = currentState.inputPassword)
is PasswordUiEvent.OnNextButtonClick -> {
when (event.type) {
PasswordType.SignUp -> checkSignUpPassword(currentState.inputPassword)
PasswordType.Session -> checkSessionPassword(currentState.inputPassword)
}
}
}
}
Expand All @@ -39,7 +44,7 @@ internal class PassWordViewModel @Inject constructor(
}
}

private fun checkPassword(password: String) = viewModelScope.launch {
private fun checkSignUpPassword(password: String) = viewModelScope.launch {
setEffect(PasswordSideEffect.KeyboardHide)
checkSignUpPasswordUseCase(password)
.onSuccess { isPasswordValid ->
Expand All @@ -62,5 +67,29 @@ internal class PassWordViewModel @Inject constructor(
}
}
}

private fun checkSessionPassword(password: String) = viewModelScope.launch {
setEffect(PasswordSideEffect.KeyboardHide)
checkSessionPasswordUseCase(password)
.onSuccess { isPasswordValid ->
when (isPasswordValid) {
true -> {
setEffect(PasswordSideEffect.NavigateToNextScreen)
}

false -> {
setState {
copy(
isWrong = true,
)
}
}
}
}.onFailure { exception ->
if (exception is FirebaseNetworkException) {
setEffect(PasswordSideEffect.ShowToast("네트워크 연결을 확인해주세요"))
}
}
}

}
Loading
Loading