Skip to content

Commit

Permalink
Merge pull request #145 from pknu-wap/feature/tgyuu/#141
Browse files Browse the repository at this point in the history
Feature/tgyuu/#141
  • Loading branch information
tgyuuAn authored Mar 3, 2024
2 parents f2e53e3 + 8a3e41b commit e64ac5f
Show file tree
Hide file tree
Showing 22 changed files with 253 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion app/src/main/java/com/wap/wapp/navigation/WappNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ fun WappNavHost(
navigateToSignUp = navController::navigateToSignUp,
)
signUpScreen(
navigateToNotice = navController::navigateToNotice,
navigateToNotice = {
navController.navigateToNotice(
navOptions { popUpTo(navController.graph.id) { inclusive = true } },
)
},
navigateToSignIn = navController::navigateToSignIn,
)
noticeScreen()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ interface AuthRepository {
suspend fun deleteUser(): Result<Unit>

suspend fun isUserSignIn(): Result<Boolean>

suspend fun checkMemberCode(code: String): Result<Boolean>
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ class AuthRepositoryImpl @Inject constructor(
override suspend fun deleteUser(): Result<Unit> = authDataSource.deleteUser()

override suspend fun isUserSignIn(): Result<Boolean> = authDataSource.isUserSignIn()

override suspend fun checkMemberCode(code: String): Result<Boolean> =
authDataSource.checkMemberCode(code)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface ManagementRepository {

suspend fun postManager(userId: String): Result<Unit>

suspend fun getManagementCode(code: String): Result<Boolean>
suspend fun checkManagementCode(code: String): Result<Boolean>

suspend fun deleteManager(userId: String): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class ManagementRepositoryImpl @Inject constructor(
override suspend fun postManager(userId: String): Result<Unit> =
managementDataSource.postManager(userId)

override suspend fun getManagementCode(code: String): Result<Boolean> =
managementDataSource.getManagementCode(code)
override suspend fun checkManagementCode(code: String): Result<Boolean> =
managementDataSource.checkManagementCode(code)

override suspend fun deleteManager(userId: String): Result<Unit> =
managementDataSource.deleteManager(userId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fun WappButton(
onClick = { onClick() },
enabled = isEnabled,
colors = ButtonDefaults.buttonColors(
contentColor = WappTheme.colors.black,
contentColor = WappTheme.colors.white,
containerColor = WappTheme.colors.yellow34,
disabledContentColor = WappTheme.colors.white,
disabledContainerColor = WappTheme.colors.grayA2,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.wap.wapp.core.domain.usecase.auth

import com.wap.wapp.core.data.repository.auth.AuthRepository
import com.wap.wapp.core.domain.model.CodeValidation
import javax.inject.Inject

class CheckMemberCodeUseCase @Inject constructor(
private val authRepository: AuthRepository,
) {
suspend operator fun invoke(code: String): Result<CodeValidation> = runCatching {
authRepository.checkMemberCode(code).fold(
onSuccess = { isValid ->
if (isValid) {
return@fold CodeValidation.VALID
}
CodeValidation.INVALID
},
onFailure = { CodeValidation.INVALID },
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class ValidateManagementCodeUseCase @Inject constructor(
class CheckManagementCodeUseCase @Inject constructor(
private val managementRepository: ManagementRepository,
private val userRepository: UserRepository,
) {
suspend operator fun invoke(code: String): Result<CodeValidation> = runCatching {
managementRepository.getManagementCode(code)
managementRepository.checkManagementCode(code)
.onSuccess { isValid ->
if (isValid.not()) { // 코드가 틀렸을 경우
return@runCatching CodeValidation.INVALID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ interface AuthDataSource {
suspend fun deleteUser(): Result<Unit>

suspend fun isUserSignIn(): Result<Boolean>

suspend fun checkMemberCode(code: String): Result<Boolean>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package com.wap.wapp.core.network.source.auth

import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseAuth.AuthStateListener
import com.google.firebase.firestore.FirebaseFirestore
import com.wap.wapp.core.network.constant.CODES_COLLECTION
import com.wap.wapp.core.network.utils.await
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
import javax.inject.Inject

class AuthDataSourceImpl @Inject constructor(
private val firebaseAuth: FirebaseAuth,
private val firebaseFirestore: FirebaseFirestore,
) : AuthDataSource {
override suspend fun signOut(): Result<Unit> = runCatching {
firebaseAuth.signOut()
Expand Down Expand Up @@ -45,4 +48,13 @@ class AuthDataSourceImpl @Inject constructor(
}
}
}

override suspend fun checkMemberCode(code: String): Result<Boolean> = runCatching {
val result = firebaseFirestore.collection(CODES_COLLECTION)
.whereEqualTo("user", code)
.get()
.await()

result.isEmpty.not()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface ManagementDataSource {

suspend fun postManager(userId: String): Result<Unit>

suspend fun getManagementCode(code: String): Result<Boolean>
suspend fun checkManagementCode(code: String): Result<Boolean>

suspend fun deleteManager(userId: String): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ManagementDataSourceImpl @Inject constructor(
.await()
}

override suspend fun getManagementCode(code: String): Result<Boolean> = runCatching {
override suspend fun checkManagementCode(code: String): Result<Boolean> = runCatching {
val result = firebaseFirestore.collection(CODES_COLLECTION)
.whereEqualTo("management", code)
.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ import com.wap.wapp.feature.auth.R.string
internal fun SignInContent(
openSignInSheet: () -> Unit,
navigateToNotice: () -> Unit,
modifier: Modifier = Modifier,
) {
val scrollState = rememberScrollState()

Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier
modifier = modifier
.fillMaxSize()
.padding(horizontal = 16.dp)
.verticalScroll(scrollState),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ internal fun SignInScreen(
}
},
navigateToNotice = { navigateToNotice() },
modifier = Modifier.addFocusCleaner(focusManager),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
Expand All @@ -34,6 +37,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.wap.designsystem.WappTheme
import com.wap.designsystem.component.WappSubTopBar
import com.wap.designsystem.modifier.addFocusCleaner
Expand All @@ -43,6 +47,7 @@ import com.wap.wapp.feature.auth.R.drawable.ic_card
import com.wap.wapp.feature.auth.R.drawable.ic_door
import com.wap.wapp.feature.auth.R.string
import com.wap.wapp.feature.auth.signup.SignUpViewModel.SignUpEvent
import com.wap.wapp.feature.auth.signup.validation.CodeValidationDialog
import kotlinx.coroutines.flow.collectLatest

@Composable
Expand All @@ -68,11 +73,18 @@ internal fun SignUpScreen(
val snackBarHostState = remember { SnackbarHostState() }
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
var showCodeValidationDialog by remember { mutableStateOf(false) }

LaunchedEffect(true) {
viewModel.signUpEventFlow.collectLatest {
when (it) {
is SignUpEvent.Success -> navigateToNotice()
is SignUpEvent.SignUpSuccess -> navigateToNotice()

is SignUpEvent.ValidateUserInformationSuccess -> {
showCodeValidationDialog = true
}

is SignUpEvent.CheckMemberCodeSuccess -> viewModel.postUserProfile()

is SignUpEvent.Failure ->
snackBarHostState.showSnackbar(message = it.throwable.toSupportingText())
Expand All @@ -92,6 +104,20 @@ internal fun SignUpScreen(
.addFocusCleaner(focusManager)
.padding(paddingValue),
) {
if (showCodeValidationDialog) {
CodeValidationDialog(
code = viewModel.memberCode.collectAsStateWithLifecycle().value,
setValidationCode = viewModel::setWapMemberCode,
onConfirmRequest = viewModel::checkMemberCode,
onDismissRequest = { showCodeValidationDialog = false },
isError = viewModel.isError.collectAsStateWithLifecycle().value,
supportingText =
stringResource(
viewModel.errorSupportingText.collectAsStateWithLifecycle().value,
),
)
}

WappSubTopBar(
modifier = Modifier
.fillMaxWidth()
Expand Down Expand Up @@ -189,7 +215,7 @@ internal fun SignUpScreen(
Spacer(modifier = Modifier.weight(1f))

Button(
onClick = { viewModel.postUserProfile() },
onClick = { viewModel.validateUserInformation() },
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,89 @@ package com.wap.wapp.feature.auth.signup

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wap.wapp.core.domain.model.CodeValidation
import com.wap.wapp.core.domain.usecase.auth.CheckMemberCodeUseCase
import com.wap.wapp.core.domain.usecase.user.PostUserProfileUseCase
import com.wap.wapp.feature.auth.R
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class SignUpViewModel @Inject constructor(
private val postUserProfileUseCase: PostUserProfileUseCase,
private val checkMemberCodeUseCase: CheckMemberCodeUseCase,
) : ViewModel() {

private val _signUpEventFlow = MutableSharedFlow<SignUpEvent>()
val signUpEventFlow: SharedFlow<SignUpEvent> get() = _signUpEventFlow
val signUpEventFlow: SharedFlow<SignUpEvent> = _signUpEventFlow.asSharedFlow()

private val _signUpName: MutableStateFlow<String> = MutableStateFlow("")
val signUpName: StateFlow<String> get() = _signUpName
val signUpName: StateFlow<String> = _signUpName.asStateFlow()

private val _signUpStudentId: MutableStateFlow<String> = MutableStateFlow("")
val signUpStudentId: StateFlow<String> get() = _signUpStudentId
val signUpStudentId: StateFlow<String> = _signUpStudentId.asStateFlow()

private val _signUpYear: MutableStateFlow<String> = MutableStateFlow("")
val signUpYear: StateFlow<String> get() = _signUpYear
val signUpYear: StateFlow<String> = _signUpYear.asStateFlow()

private val _signUpSemester: MutableStateFlow<String> = MutableStateFlow(FIRST_SEMESTER)
val signUpSemester: StateFlow<String> get() = _signUpSemester
val signUpSemester: StateFlow<String> = _signUpSemester.asStateFlow()

private val _memberCode: MutableStateFlow<String> = MutableStateFlow("")
val memberCode: StateFlow<String> = _memberCode.asStateFlow()

private val _isError: MutableStateFlow<Boolean> = MutableStateFlow(false)
val isError: StateFlow<Boolean> get() = _isError

fun postUserProfile() = viewModelScope.launch {
private val _errorSupportingText: MutableStateFlow<Int> =
MutableStateFlow(R.string.sign_up_dialog_hint)
val errorSupportingText: StateFlow<Int> = _errorSupportingText.asStateFlow()

fun validateUserInformation() = viewModelScope.launch {
if (!isValidStudentId()) {
_signUpEventFlow.emit(
SignUpEvent.Failure(IllegalStateException("학번은 9자리로만 입력하실 수 있어요!")),
)
return@launch
}

postUserProfileUseCase(
userName = _signUpName.value,
studentId = _signUpStudentId.value,
registeredAt = "${_signUpYear.value} ${_signUpSemester.value}",
).onSuccess {
_signUpEventFlow.emit(SignUpEvent.Success)
_signUpEventFlow.emit(SignUpEvent.ValidateUserInformationSuccess)
}

fun checkMemberCode() = viewModelScope.launch {
checkMemberCodeUseCase(_memberCode.value).onSuccess {
when (it) {
CodeValidation.VALID ->
_signUpEventFlow.emit(SignUpEvent.CheckMemberCodeSuccess)

CodeValidation.INVALID -> {
_isError.value = true
_errorSupportingText.value = R.string.sign_up_incorrect_code
}
}
}.onFailure { throwable ->
_isError.value = true
_signUpEventFlow.emit(SignUpEvent.Failure(throwable))
}
}

suspend fun postUserProfile() = postUserProfileUseCase(
userName = _signUpName.value,
studentId = _signUpStudentId.value,
registeredAt = "${_signUpYear.value} ${_signUpSemester.value}",
).onSuccess {
_signUpEventFlow.emit(SignUpEvent.SignUpSuccess)
}.onFailure { throwable ->
_signUpEventFlow.emit(SignUpEvent.Failure(throwable))
_isError.value = true
}

fun isValidStudentId(): Boolean = (_signUpStudentId.value.length == STUDENT_ID_LENGTH)

fun setName(name: String) { _signUpName.value = name }
Expand All @@ -60,8 +95,12 @@ class SignUpViewModel @Inject constructor(

fun setSemester(semester: String) { _signUpSemester.value = semester }

fun setWapMemberCode(code: String) { _memberCode.value = code }

sealed class SignUpEvent {
data object Success : SignUpEvent()
data object ValidateUserInformationSuccess : SignUpEvent()
data object CheckMemberCodeSuccess : SignUpEvent()
data object SignUpSuccess : SignUpEvent()
data class Failure(val throwable: Throwable) : SignUpEvent()
}

Expand Down
Loading

0 comments on commit e64ac5f

Please sign in to comment.