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

[6주차 과제] 서버 통신 심화 #11

Merged
merged 23 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2e8058d
[FEAT/#10] ViewModel, LiveData로 회원가입, 로그인 결과 처리
leeeha May 20, 2023
2248aa7
[FEAT/#10] 네트워크 통신에 로깅 인터셉터 추가
leeeha May 21, 2023
b266e26
[MERGE] week4, week6 브랜치 머지
leeeha May 30, 2023
592554e
[DOCS] 커밋 메시지 템플릿 추가
leeeha May 30, 2023
5a84a35
[CHORE] 부연 설명이 필요한 부분 주석 추가
leeeha Jun 2, 2023
d39751d
[DESIGN] EditText 대신 Text field 사용하기
leeeha Jun 2, 2023
ff461d7
[FEAT] 아이디, 비밀번호 입력값에 따라 경고 문구 띄우기
leeeha Jun 2, 2023
21eee1e
[FEAT] 모든 입력값이 유효한 경우에만 버튼 활성화 시키기 (디폴트는 비활성화)
leeeha Jun 2, 2023
2e98d37
[FEAT] 입력값 삭제하는 clear 버튼 추가
leeeha Jun 2, 2023
d74edf4
[DESIGN] 로그인 화면 텍스트 필드 디자인 변경
leeeha Jun 2, 2023
12250ef
[FIX] 특수문자 - 앞에서는 역슬래쉬를 붙여야 한다.
leeeha Jun 2, 2023
f2514b9
[FIX] 입력값에 따라 동적으로 버튼 상태가 달라지도록 변경
leeeha Jun 2, 2023
f3e888c
[REFACTOR] 마이페이지 화면에서 shared pref 관련 로직 뷰모델로 분리하기
leeeha Jun 4, 2023
3388720
[DESIGN] 회원가입 입력란 텍스트 크기 조정
leeeha Jun 4, 2023
74238bf
[FEAT] 리스트 보여줄 때 로딩 다이얼로그 띄우기
leeeha Jun 4, 2023
e300307
[REFACTOR] 회원가입 아이디, 비밀번호 정규표현식 상수화 시키기
leeeha Jun 11, 2023
2283def
[REFACTOR] addTextChangedListener 함수 내부에서 중복되는 로직 모듈화
leeeha Jun 11, 2023
fc2aa58
[REFACTOR] deprecated 된 singleLine 대신 maxLines 속성 사용하도록 변경
leeeha Jun 11, 2023
d1a9608
[REFACTOR] 데이터바인딩으로 라이브데이터 관찰하여 입력 상태에 따라 에러 메시지 표시
leeeha Jun 11, 2023
31eb6f0
[FEAT] DialogFragment 이용해서 커스텀 로그아웃 다이얼로그 구현
leeeha Jun 11, 2023
f6389f1
[DESIGN] 로딩 다이얼로그 레이아웃 구현
leeeha Jun 11, 2023
d5d77e8
[FEAT] DialogFragment 이용하여 로딩 다이얼로그 구현, UiState에 Loading 추가하고 라이브데이터로…
leeeha Jun 14, 2023
d9051c5
[FIX] 회원가입 각 항목이 비어있을 때는 에러 메시지가 표시되지 않도록 변경
leeeha Jun 14, 2023
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
10 changes: 5 additions & 5 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

<activity
android:name=".presentation.login.LoginActivity"
android:exported="true">
android:exported="true"
android:windowSoftInputMode="adjustPan">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -32,14 +33,13 @@

<activity
android:name=".presentation.signup.SignUpActivity"
android:exported="false" />
android:exported="false"
android:windowSoftInputMode="adjustPan" />

<activity
android:name=".presentation.main.MainActivity"
android:exported="true" >

android:exported="false" />

</activity>
</application>

</manifest>
1,545 changes: 1,539 additions & 6 deletions app/src/main/assets/mock_repo_list.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions app/src/main/java/org/android/go/sopt/GoSoptApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class GoSoptApplication : Application() {
}.getOrNull()
}

// assets은 context 참조가 필요하기 때문에
// 뷰모델이 아니라 application 부분에서 함수를 정의했다.
private fun loadAsset(fileName: String): String {
return assets.open(fileName).use { inputStream ->
val size = inputStream.available()
Expand Down Expand Up @@ -53,8 +55,6 @@ class GoSoptApplication : Application() {
}
}

// 클래스가 처음 로딩될 때 초기화 되는 companion object
// 프로그램과 생명주기를 같이 하며, 한번만 생성된다. (싱글톤)
companion object {
lateinit var prefs: SharedPreferences
var mockJsonString: String? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ class LoginActivity : BindingActivity<ActivityLoginBinding>(R.layout.activity_lo
}

private fun clearEditText() {
binding.etId.text.clear()
binding.etPw.text.clear()
binding.etId.text?.clear()
binding.etPw.text?.clear()
}

private fun focusOutEditText(button: View?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,31 @@ import org.android.go.sopt.R
import org.android.go.sopt.util.binding.BindingFragment
import org.android.go.sopt.databinding.FragmentGalleryBinding
import org.android.go.sopt.presentation.main.gallery.adapter.FollowerAdapter
import org.android.go.sopt.util.LoadingDialog
import org.android.go.sopt.util.extension.showSnackbar
import org.android.go.sopt.util.state.RemoteUiState.*

/** ReqRes API Retrofit + ListAdapter */
class GalleryFragment : BindingFragment<FragmentGalleryBinding>(R.layout.fragment_gallery) {
private val viewModel by viewModels<GalleryViewModel>()
private lateinit var loadingDialog: LoadingDialog
private var followerAdapter: FollowerAdapter? = null

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.vm = viewModel

showLoadingDialog()
initFollowerAdapter()
initRecyclerViewLayoutManager()
initFollowerListStateObserver()
}

private fun showLoadingDialog() {
loadingDialog = LoadingDialog(requireContext())
loadingDialog.show()
}

private fun initFollowerAdapter() {
followerAdapter = FollowerAdapter()
binding.rvGallery.adapter = followerAdapter
Expand All @@ -37,7 +45,10 @@ class GalleryFragment : BindingFragment<FragmentGalleryBinding>(R.layout.fragmen
private fun initFollowerListStateObserver() {
viewModel.getFollowerListState.observe(viewLifecycleOwner) { state ->
when (state) {
is Success -> followerAdapter?.submitList(viewModel.followerList)
is Success -> {
followerAdapter?.submitList(viewModel.followerList)
if(loadingDialog.isShowing) loadingDialog.dismiss()
}
is Failure -> requireContext().showSnackbar(
binding.root,
getString(R.string.gallery_follower_list_null_msg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failure인 경우에도 LoadingDialog를 dismiss 해주는 것이 좋을 것 같습니다!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ class GalleryViewModel : ViewModel() {

companion object {
private const val PAGE = 1
private const val PER_PAGE = 10
private const val PER_PAGE = 12
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.android.go.sopt.databinding.FragmentHomeBinding
import org.android.go.sopt.data.entity.MultiViewItem
import org.android.go.sopt.data.entity.MultiViewItem.Header
import org.android.go.sopt.presentation.main.home.adapter.MultiViewAdapter
import org.android.go.sopt.util.LoadingDialog
import org.android.go.sopt.util.binding.BindingFragment
import org.android.go.sopt.util.extension.showSnackbar
import org.android.go.sopt.util.state.LocalUiState.Failure
Expand All @@ -17,17 +18,24 @@ import org.android.go.sopt.util.state.LocalUiState.Success
class HomeFragment : BindingFragment<FragmentHomeBinding>(R.layout.fragment_home) {
private val viewModel by viewModels<HomeViewModel>()
private lateinit var multiViewItems: MutableList<MultiViewItem>
private lateinit var loadingDialog: LoadingDialog
private var multiViewAdapter: MultiViewAdapter? = null

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.vm = viewModel

showLoadingDialog()
initMultiViewItems()
initRepoListStateObserver()
initMultiViewAdapter()
}

private fun showLoadingDialog() {
loadingDialog = LoadingDialog(requireContext())
loadingDialog.show()
}

private fun initMultiViewItems() {
multiViewItems = mutableListOf()
multiViewItems.add(Header(getString(R.string.header_text)))
Expand All @@ -36,7 +44,10 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>(R.layout.fragment_home
private fun initRepoListStateObserver() {
viewModel.getRepoListState.observe(viewLifecycleOwner) { state ->
when (state) {
is Success -> addRepoList()
is Success -> {
addRepoList()
if(loadingDialog.isShowing) loadingDialog.dismiss()
}
is Failure -> requireContext().showSnackbar(
binding.root,
getString(R.string.home_get_repo_list_fail_msg)
Expand All @@ -45,11 +56,6 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>(R.layout.fragment_home
}
}

private fun initMultiViewAdapter() {
multiViewAdapter = MultiViewAdapter(multiViewItems)
binding.rvHome.adapter = multiViewAdapter
}

private fun addRepoList() {
viewModel.repoList?.let { repoList ->
for (element in repoList) {
Expand All @@ -58,6 +64,11 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>(R.layout.fragment_home
}
}

private fun initMultiViewAdapter() {
multiViewAdapter = MultiViewAdapter(multiViewItems)
binding.rvHome.adapter = multiViewAdapter
}

fun scrollToTop() {
binding.rvHome.scrollToPosition(0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,20 @@ import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.viewModels
import org.android.go.sopt.R
import org.android.go.sopt.util.binding.BindingFragment
import org.android.go.sopt.databinding.FragmentMyPageBinding
import org.android.go.sopt.presentation.login.LoginActivity
import org.android.go.sopt.util.PreferenceManager

class MyPageFragment : BindingFragment<FragmentMyPageBinding>(R.layout.fragment_my_page) {
private val preferenceManager = PreferenceManager()
private val viewModel by viewModels<MyPageViewModel>()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initUserProfile()
initLogoutButtonClickListener()
}
binding.vm = viewModel

private fun initUserProfile() {
val signedUpUser = preferenceManager.signedUpUser
if (signedUpUser != null) {
binding.tvName.append(signedUpUser.name)
binding.tvHobby.append(signedUpUser.hobby)
}
initLogoutButtonClickListener()
}

private fun initLogoutButtonClickListener() {
Expand All @@ -37,7 +30,7 @@ class MyPageFragment : BindingFragment<FragmentMyPageBinding>(R.layout.fragment_
val builder = AlertDialog.Builder(requireContext())
builder.setMessage(R.string.dialog_logout_msg)
.setPositiveButton(R.string.dialog_yes) { dialog, id ->
preferenceManager.loginState = false
viewModel.logout()
navigateToLoginScreen()
}
.setNegativeButton(R.string.dialog_no, null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.android.go.sopt.presentation.main.mypage

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.android.go.sopt.data.entity.User
import org.android.go.sopt.util.PreferenceManager

class MyPageViewModel : ViewModel() {
private val preferenceManager = PreferenceManager()
private val _signedUpUser = MutableLiveData<User>()
val signedUpUser: User?
get() = _signedUpUser.value

init {
getSignedUpUser()
}

private fun getSignedUpUser() {
_signedUpUser.value = preferenceManager.signedUpUser
}

fun logout() {
preferenceManager.loginState = false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ package org.android.go.sopt.presentation.signup

import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener
import org.android.go.sopt.R
import org.android.go.sopt.util.binding.BindingActivity
import org.android.go.sopt.databinding.ActivitySignUpBinding
import org.android.go.sopt.presentation.login.LoginActivity
import org.android.go.sopt.util.code.CODE_DUPLICATED_ID
import org.android.go.sopt.util.code.CODE_INCORRECT_INPUT
import org.android.go.sopt.util.code.CODE_INVALID_INPUT
import org.android.go.sopt.util.extension.hideKeyboard
import org.android.go.sopt.util.extension.showSnackbar
Expand All @@ -24,10 +22,60 @@ class SignUpActivity : BindingActivity<ActivitySignUpBinding>(R.layout.activity_
binding.vm = viewModel

initRootLayoutClickListener()
initEditTextChangedListeners()
initEditTextChangedListener()
initSignUpStateObserver()
}

private fun initEditTextChangedListener() {
binding.etId.addTextChangedListener {
if (!viewModel.isValidId()) {
binding.tilId.error = getString(R.string.sign_up_id_helper_text)
deactivateSignUpButton()
} else {
binding.tilId.error = null
if (viewModel.isValidInput()) activateSignUpButton()
}
}

binding.etPw.addTextChangedListener {
if (!viewModel.isValidPw()) {
binding.tilPw.error = getString(R.string.sign_up_pw_helper_text)
deactivateSignUpButton()
} else {
binding.tilPw.error = null
if (viewModel.isValidInput()) activateSignUpButton()
}
}

binding.etName.addTextChangedListener {
if (viewModel.isNotBlankName()) {
binding.tilName.error = null
if (viewModel.isValidInput()) activateSignUpButton()
} else {
binding.tilName.error = getString(R.string.sign_up_required_input_err)
deactivateSignUpButton()
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TextChangedListener를 설정할 때 유사한 로직이 반복되고 있는데, InputEditText와 Boolean 타입의 조건을 인자로 받는 함수를 만들어 사용할 수도 있을 것 같습니다!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한개로 합칠수 있을거 같습니다


binding.etHobby.addTextChangedListener {
if (viewModel.isNotBlankHobby()) {
binding.tilHobby.error = null
if (viewModel.isValidInput()) activateSignUpButton()
} else {
binding.tilHobby.error = getString(R.string.sign_up_required_input_err)
deactivateSignUpButton()
}
}
}

private fun activateSignUpButton() {
binding.btnSignUp.isEnabled = true
}

private fun deactivateSignUpButton() {
binding.btnSignUp.isEnabled = false
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것도 데이터바인딩이나 바인딩 어댑터를 활용하여 처리하면 코드를 좀 줄일 수 있을 것 같습니다!


private fun initSignUpStateObserver() {
viewModel.signUpState.observe(this) { state ->
when (state) {
Expand All @@ -38,10 +86,6 @@ class SignUpActivity : BindingActivity<ActivitySignUpBinding>(R.layout.activity_
binding.root,
getString(R.string.invalid_input_error_msg)
)
CODE_INCORRECT_INPUT -> showSnackbar(
binding.root,
getString(R.string.incorrect_input_error_msg)
)
CODE_DUPLICATED_ID -> showSnackbar(
binding.root,
getString(R.string.id_duplicate_error_msg)
Expand All @@ -63,40 +107,16 @@ class SignUpActivity : BindingActivity<ActivitySignUpBinding>(R.layout.activity_
private fun initRootLayoutClickListener() {
binding.root.setOnClickListener {
hideKeyboard()
focusOutEditText()
}
}

private fun initEditTextChangedListeners() {
binding.etId.addTextChangedListener {
if (!viewModel.isValidId()) {
binding.tvIdLimitError.visibility = View.VISIBLE
} else {
binding.tvIdLimitError.visibility = View.INVISIBLE
}
}

binding.etPw.addTextChangedListener {
if (!viewModel.isValidPw()) {
binding.tvPwLimitError.visibility = View.VISIBLE
} else {
binding.tvPwLimitError.visibility = View.INVISIBLE
}
}

binding.etName.addTextChangedListener {
if (viewModel.name.isEmpty()) {
binding.tvNameEmptyError.visibility = View.VISIBLE
} else {
binding.tvNameEmptyError.visibility = View.INVISIBLE
}
}

binding.etHobby.addTextChangedListener {
if (viewModel.hobby.isEmpty()) {
binding.tvHobbyEmptyError.visibility = View.VISIBLE
} else {
binding.tvHobbyEmptyError.visibility = View.INVISIBLE
}
private fun focusOutEditText() {
with(binding) {
etId.clearFocus()
etPw.clearFocus()
etName.clearFocus()
etHobby.clearFocus()
}
}
}
Loading