-
Notifications
You must be signed in to change notification settings - Fork 0
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/#7 week04] #8
base: develop
Are you sure you want to change the base?
Changes from all commits
d5b8ded
9e8ad27
e3d474b
b1af6ba
ba387b5
95d104a
3d55464
27b13a5
c9c6566
d2cc362
e0cc77e
465839e
f8761a1
6b25e42
44c44a5
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 |
---|---|---|
@@ -1,27 +1,51 @@ | ||
package org.sopt.and.data | ||
|
||
import android.content.Context | ||
import org.sopt.and.network.UserService | ||
import org.sopt.and.network.request.RequestLoginDto | ||
import org.sopt.and.network.request.RequestSignUpDto | ||
import org.sopt.and.network.response.ResponseDto | ||
import org.sopt.and.network.response.ResponseHobbyDto | ||
import retrofit2.Response | ||
|
||
class UserRepository(context: Context) { | ||
class UserRepository( | ||
private val userService: UserService, | ||
context: Context) { | ||
private val dataSource = DataSource(context.getSharedPreferences("UserPrefs", Context.MODE_PRIVATE)) | ||
|
||
fun saveUserInfo(email: String, password: String) { | ||
dataSource.saveUserInfo(email, password) | ||
suspend fun postSignUp(username: String, password: String, hobby: String): Response<ResponseDto> { | ||
val request = RequestSignUpDto(username, password, hobby) | ||
return userService.postSignUp(request) | ||
} | ||
Comment on lines
+16
to
19
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. 이렇게 간단하게 api 요청 코드를 작성할 수 있군요! 배워갑니당 |
||
|
||
fun getEmail(): String? { | ||
return dataSource.getEmail() | ||
suspend fun postLogin(username: String, password: String): Response<ResponseDto> { | ||
val request = RequestLoginDto(username, password) | ||
return userService.postLogin(request) | ||
} | ||
|
||
fun getPassword(): String? { | ||
return dataSource.getPassword() | ||
suspend fun getHobby(token: String): Response<ResponseHobbyDto> { | ||
return userService.getHobby(token) | ||
} | ||
|
||
fun saveUserInfo(username: String, password: String, hobby: String) { | ||
dataSource.saveUserInfo(username, password, hobby) | ||
} | ||
|
||
fun saveUserToken(token: String) { | ||
dataSource.saveUserToken(token) | ||
} | ||
|
||
fun getUsername(): String? = dataSource.getUsername() | ||
fun getPassword(): String? = dataSource.getPassword() | ||
fun getHobby(): String? = dataSource.getHobby() | ||
fun getToken(): String? = dataSource.getToken() | ||
|
||
fun isLoggedIn(): Boolean { | ||
return dataSource.isLoggedIn() | ||
} | ||
|
||
fun logout() { | ||
dataSource.clearUserCredentials() | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
package org.sopt.and.feature.login | ||
|
||
sealed class LoginEvent { | ||
data class Success(val email: String) : LoginEvent() | ||
data class Failure(val message: String) : LoginEvent() | ||
data class Success(val token: String) : LoginEvent() | ||
data class Failure(val message: String) : LoginEvent() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,11 +57,11 @@ fun LoginRoute( | |
val snackbarHostState = remember { SnackbarHostState() } | ||
val coroutineScope = rememberCoroutineScope() | ||
|
||
var email by remember { mutableStateOf("") } | ||
var username by remember { mutableStateOf("") } | ||
var password by remember { mutableStateOf("") } | ||
var ispasswordVisible by remember { mutableStateOf(false) } | ||
var isemailErrorVisible by remember { mutableStateOf(false) } | ||
var ispasswordErrorVisible by remember { mutableStateOf(false) } | ||
var isPasswordVisible by remember { mutableStateOf(false) } | ||
var isUsernameErrorVisible by remember { mutableStateOf(false) } | ||
var isPasswordErrorVisible by remember { mutableStateOf(false) } | ||
|
||
LaunchedEffect(viewModel.loginEvent) { | ||
viewModel.loginEvent.collect { event -> | ||
|
@@ -82,19 +82,20 @@ fun LoginRoute( | |
} | ||
|
||
LoginScreen( | ||
email = email, | ||
onEmailChange = { email = it }, | ||
username = username, | ||
onUsernameChange = { username = it }, | ||
password = password, | ||
onPasswordChange = { password = it }, | ||
passwordVisible = ispasswordVisible, | ||
onPasswordVisibilityChange = { ispasswordVisible = !ispasswordVisible }, | ||
onLoginClick = { viewModel.onLoginClick(email, password) }, | ||
passwordErrorVisible = ispasswordErrorVisible, | ||
onEmailFocusChanged = { isFocused -> | ||
isemailErrorVisible = isFocused && email.isEmpty() | ||
passwordVisible = isPasswordVisible, | ||
onPasswordVisibilityChange = { isPasswordVisible = !isPasswordVisible }, | ||
onLoginClick = { viewModel.onLoginClick(username, password) }, | ||
usernameErrorVisible = isUsernameErrorVisible, | ||
passwordErrorVisible = isPasswordErrorVisible, | ||
onUsernameFocusChanged = { isFocused -> | ||
isUsernameErrorVisible = isFocused && username.isEmpty() | ||
}, | ||
onPasswordFocusChanged = { isFocused -> | ||
ispasswordErrorVisible = isFocused && password.isEmpty() | ||
isPasswordErrorVisible = isFocused && password.isEmpty() | ||
}, | ||
onNavigateToSignUp = { navController.navigate("signup") }, | ||
snackbarHostState = snackbarHostState | ||
|
@@ -103,20 +104,21 @@ fun LoginRoute( | |
|
||
@Composable | ||
fun LoginScreen( | ||
email: String, | ||
onEmailChange: (String) -> Unit, | ||
username: String, | ||
onUsernameChange: (String) -> Unit, | ||
password: String, | ||
onPasswordChange: (String) -> Unit, | ||
passwordVisible: Boolean, | ||
onPasswordVisibilityChange: () -> Unit, | ||
onLoginClick: () -> Unit, | ||
usernameErrorVisible: Boolean, | ||
passwordErrorVisible: Boolean, | ||
onEmailFocusChanged: (Boolean) -> Unit, | ||
onUsernameFocusChanged: (Boolean) -> Unit, | ||
onPasswordFocusChanged: (Boolean) -> Unit, | ||
onNavigateToSignUp: () -> Unit, | ||
snackbarHostState: SnackbarHostState | ||
) { | ||
val emailFocusRequester = remember { FocusRequester() } | ||
val usernameFocusRequester = remember { FocusRequester() } | ||
val passwordFocusRequester = remember { FocusRequester() } | ||
val loginButtonFocusRequester = remember { FocusRequester() } | ||
|
||
|
@@ -139,15 +141,15 @@ fun LoginScreen( | |
) | ||
|
||
WavveTextField( | ||
value = email, | ||
onValueChange = onEmailChange, | ||
placeholder = "이메일 입력", | ||
value = username, | ||
onValueChange = onUsernameChange, | ||
placeholder = "사용자 이름 입력", | ||
onFocusChanged = { isFocused -> | ||
onEmailFocusChanged(isFocused) | ||
onUsernameFocusChanged(isFocused) | ||
}, | ||
Comment on lines
147
to
149
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.
이렇게 줄일 수 있을 것 같습니다 ! |
||
onNext = { passwordFocusRequester.requestFocus() }, | ||
keyboardOptions = KeyboardOptions.Default.copy(imeAction = Next), | ||
modifier = Modifier.focusRequester(emailFocusRequester) | ||
modifier = Modifier.focusRequester(usernameFocusRequester) | ||
) | ||
|
||
Spacer(modifier = Modifier.height(16.dp)) | ||
|
@@ -201,4 +203,4 @@ fun LoginScreen( | |
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp) | ||
) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,22 +11,25 @@ class LoginViewModel(private val userRepository: UserRepository) : ViewModel() { | |
private val _loginEvent = MutableSharedFlow<LoginEvent>() | ||
val loginEvent = _loginEvent.asSharedFlow() | ||
|
||
fun onLoginClick(email: String, password: String) { | ||
fun onLoginClick(username: String, password: String) { | ||
viewModelScope.launch { | ||
val savedEmail = userRepository.getEmail() | ||
val savedPassword = userRepository.getPassword() | ||
|
||
if (email != savedEmail) { | ||
_loginEvent.emit(LoginEvent.Failure("이메일이 다릅니다.")) | ||
return@launch | ||
} | ||
|
||
if (password != savedPassword) { | ||
_loginEvent.emit(LoginEvent.Failure("비밀번호가 틀렸습니다.")) | ||
return@launch | ||
runCatching { | ||
userRepository.postLogin(username, password) | ||
}.onSuccess { response -> | ||
if (response.isSuccessful && response.body() != null) { | ||
val token = response.body()?.result?.token | ||
if (token != null) { | ||
userRepository.saveUserToken(token) | ||
_loginEvent.emit(LoginEvent.Success(token)) | ||
} else { | ||
_loginEvent.emit(LoginEvent.Failure("토큰을 받아오지 못했습니다.")) | ||
} | ||
Comment on lines
+19
to
+26
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. 해당 if 문 검증과 _loginEvent.emit 을 하기까지의 depth 를 뷰모델을 거치기전에 해소할 수 있으면 좋을 것 같아요! 그저 token을 받거나, 실패의 동작만 처리하는 동작이 있으면 좋을 것 같아요! 어디에서 해당 처리를 해주면 좋을까요!? |
||
} else { | ||
_loginEvent.emit(LoginEvent.Failure("로그인 실패: ${response.errorBody()?.string() ?: "알 수 없는 오류"}")) | ||
} | ||
}.onFailure { exception -> | ||
_loginEvent.emit(LoginEvent.Failure("네트워크 오류 발생: ${exception.message}")) | ||
} | ||
|
||
_loginEvent.emit(LoginEvent.Success(email)) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.fillMaxSize | |
import androidx.compose.foundation.layout.navigationBarsPadding | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.Modifier | ||
import androidx.navigation.compose.NavHost | ||
import androidx.navigation.compose.composable | ||
|
@@ -22,6 +24,7 @@ import org.sopt.and.feature.mypage.MyPageViewModel | |
import org.sopt.and.feature.search.SearchRoute | ||
import org.sopt.and.feature.signup.SignUpRoute | ||
import org.sopt.and.feature.signup.SignUpViewModel | ||
import org.sopt.and.network.ServicePool.userService | ||
import org.sopt.and.ui.theme.ANDANDROIDTheme | ||
|
||
class MainActivity : ComponentActivity() { | ||
|
@@ -30,19 +33,28 @@ class MainActivity : ComponentActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
enableEdgeToEdge() | ||
userRepository = UserRepository(this) | ||
userRepository = UserRepository(userService, applicationContext) | ||
|
||
setContent { | ||
val isLoggedIn = remember { mutableStateOf(userRepository.isLoggedIn()) } | ||
|
||
ANDANDROIDTheme { | ||
ChangeStatusBarColor() | ||
val navController = rememberNavController() | ||
|
||
navController.addOnDestinationChangedListener { _, _, _ -> | ||
isLoggedIn.value = userRepository.isLoggedIn() | ||
} | ||
Comment on lines
+36
to
+47
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. 로그인 여부에 따라서 bottomNav 여부를 가져갈 수도 있군요! 특정 화면에서만 BottomNav 가져가도록 하는게 어떨까 싶습니다. (개인적인 의견입니다!) |
||
|
||
|
||
Scaffold( | ||
modifier = Modifier.fillMaxSize(), | ||
bottomBar = { | ||
if (userRepository.isLoggedIn()) { | ||
BottomNavigationBar(navController = navController) | ||
Modifier.navigationBarsPadding() | ||
if (isLoggedIn.value) { | ||
BottomNavigationBar( | ||
navController = navController, | ||
modifier = Modifier.navigationBarsPadding() | ||
) | ||
} | ||
} | ||
) { innerPadding -> | ||
|
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.
이렇게 사용해도 되고 아니면,
이렇게도 사용할 수 있을 것 같아요 !
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.
오호 이렇게도 쓸 수 있군요