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주차 세미나 과제 #12

Merged
merged 18 commits into from
Jun 14, 2023
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
5 changes: 4 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ dependencies {
implementation "androidx.appcompat:appcompat:1.6.1"

// material
implementation "com.google.android.material:material:1.8.0"
implementation "com.google.android.material:material:1.9.0"

// constraint layout
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
Expand Down Expand Up @@ -107,4 +107,7 @@ dependencies {

// splash screen
implementation "androidx.core:core-splashscreen:1.0.1"

// lottie
implementation 'com.airbnb.android:lottie:3.7.1'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.android.go.sopt.presentation.dialog

import android.os.Bundle
import android.view.View
import org.android.go.sopt.R
import org.android.go.sopt.databinding.FragmentLoadingDialogBinding
import org.android.go.sopt.util.binding.BindingDialogFragment

class LoadingDialogFragment :

Choose a reason for hiding this comment

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

혹시 LoadingDialogFragment의 BindingDialogFragment를 따로 만들어둔 특별한 이유가 있는지 궁금합니당 어차피 하나의 LoadingDialogFragment로만 계속 사용하지 않나용?

Copy link
Member Author

Choose a reason for hiding this comment

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

로그아웃 경고 문구 구현할 때에도 BindingDialogFragment를 활용했었습니당
다른 DialogFragment가 추가되는 경우를 고려하면 유지보수에도 좋을 것 같네요!

BindingDialogFragment<FragmentLoadingDialogBinding>(R.layout.fragment_loading_dialog) {
Copy link
Member

Choose a reason for hiding this comment

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

오호 다이얼로그 프래그먼트도 이렇게 base를 만들어서 사용할 수 있네요! 리팩토링 때 반영하겠습니다 :)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

initDialogCancelable()
}

private fun initDialogCancelable() {
dialog?.setCancelable(false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.activity.viewModels
import dagger.hilt.android.AndroidEntryPoint
import org.android.go.sopt.R
import org.android.go.sopt.databinding.ActivityLoginBinding
import org.android.go.sopt.presentation.dialog.LoadingDialogFragment
import org.android.go.sopt.presentation.main.MainActivity
import org.android.go.sopt.presentation.signup.SignupActivity
import org.android.go.sopt.util.binding.BindingActivity
Expand All @@ -16,12 +17,15 @@ import org.android.go.sopt.util.extension.showSnackbar
import org.android.go.sopt.util.extension.showToast
import org.android.go.sopt.util.state.RemoteUiState.Error
import org.android.go.sopt.util.state.RemoteUiState.Failure
import org.android.go.sopt.util.state.RemoteUiState.Loading
Copy link
Member

@leeeha leeeha Jun 4, 2023

Choose a reason for hiding this comment

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

Loading도 UiState 중 하나로 분류해서 하니까 코드가 간결하네요! 구웃~~

import org.android.go.sopt.util.state.RemoteUiState.Success

@AndroidEntryPoint
class LoginActivity : BindingActivity<ActivityLoginBinding>(R.layout.activity_login) {
private val viewModel by viewModels<LoginViewModel>()

private val loadingDialog by lazy { LoadingDialogFragment() }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.vm = viewModel
Expand All @@ -46,18 +50,41 @@ class LoginActivity : BindingActivity<ActivityLoginBinding>(R.layout.activity_lo
private fun setupLoginState() {
viewModel.loginState.observe(this) { state ->
when (state) {
is Success -> navigateToMainScreen()
is Failure -> showSnackbar(binding.root, getString(R.string.wrong_input_msg))
is Error -> showSnackbar(binding.root, getString(R.string.unknown_error_msg))
is Loading -> {
loadingDialog.show(supportFragmentManager, TAG_LOADING_DIALOG)
}

is Success -> {
dismissLoadingDialog()
navigateToMainScreen()
}

is Failure -> {
dismissLoadingDialog()
showSnackbar(binding.root, getString(R.string.wrong_input_msg))
}

is Error -> {
dismissLoadingDialog()
showSnackbar(binding.root, getString(R.string.unknown_error_msg))
}
}
}
}
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 dismissLoadingDialog() {
if (loadingDialog.isAdded) loadingDialog.dismiss()
}

private fun navigateToMainScreen() {
showToast(getString(R.string.login_login_success_msg))
Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(this)
}
}

companion object {
const val TAG_LOADING_DIALOG = "LOADING_DIALOG"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.android.go.sopt.domain.repository.AuthRepository
import org.android.go.sopt.util.state.RemoteUiState
import org.android.go.sopt.util.state.RemoteUiState.Error
import org.android.go.sopt.util.state.RemoteUiState.Failure
import org.android.go.sopt.util.state.RemoteUiState.Loading
import org.android.go.sopt.util.state.RemoteUiState.Success
import retrofit2.HttpException
import timber.log.Timber
Expand Down Expand Up @@ -43,19 +44,19 @@ class LoginViewModel @Inject constructor(
}
}

private fun isValidInput() = id.isNotBlank() && pwd.isNotBlank()

fun login() {
if (!isValidInput()) {
_loginState.value = Failure(null)
return
}

val requestPostSignInDto = RequestPostSignInDto(
id = id,
password = pwd,
)
viewModelScope.launch {
_loginState.value = Loading

if (!isValidInput()) {
_loginState.value = Failure(CODE_INVALID_INPUT)
return@launch

Choose a reason for hiding this comment

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

혹시 이거는 무슨 의미인지 알 수 있을까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

로그인 할 때 아이디랑 비밀번호 중 하나라도 비워져있으면 오류 메시지를 띄우도록 하는 코드입니다!

}

val requestPostSignInDto = RequestPostSignInDto(
id = id,
password = pwd,
)
authRepository.postSignin(requestPostSignInDto)
.onSuccess { response ->
authRepository.setSignedUpUser(
Expand All @@ -82,6 +83,8 @@ class LoginViewModel @Inject constructor(
}
}

private fun isValidInput() = id.isNotBlank() && pwd.isNotBlank()

companion object {
const val CODE_INVALID_INPUT = 400
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,52 @@ import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
import dagger.hilt.android.AndroidEntryPoint
import org.android.go.sopt.R
import org.android.go.sopt.databinding.FragmentFollowerBinding
import org.android.go.sopt.presentation.dialog.LoadingDialogFragment
import org.android.go.sopt.presentation.login.LoginActivity.Companion.TAG_LOADING_DIALOG
import org.android.go.sopt.presentation.main.follower.FollowerAdapter.Companion.VIEW_TYPE_HEADER
import org.android.go.sopt.util.binding.BindingFragment
import org.android.go.sopt.util.extension.showSnackbar
import org.android.go.sopt.util.state.RemoteUiState.Error
import org.android.go.sopt.util.state.RemoteUiState.Failure
import org.android.go.sopt.util.state.RemoteUiState.Loading
import org.android.go.sopt.util.state.RemoteUiState.Success

@AndroidEntryPoint
class FollowerFragment : BindingFragment<FragmentFollowerBinding>(R.layout.fragment_follower) {
private val viewModel by viewModels<FollowerViewModel>()

private var followerAdapter: FollowerAdapter? = null
private var _followerAdapter: FollowerAdapter? = null
private val followerAdapter
get() = requireNotNull(_followerAdapter) { getString(R.string.adapter_not_initialized_error_msg) }

private var _loadingDialog: LoadingDialogFragment? = null
private val loadingDialog
get() = requireNotNull(_loadingDialog) { getString(R.string.dialog_not_initialized_error_msg) }
Copy link
Member

Choose a reason for hiding this comment

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

오 이렇게 backing properties를 사용하니까 변수에 대한 불변성과 널 안정성이 더 높아진 거 같네요! 👍


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

initFollowerAdapter()
initLoadingDialogFragment()
initRecyclerViewLayoutManager()
setupGetFollowerListState()
}

private fun initFollowerAdapter() {
followerAdapter = FollowerAdapter()
_followerAdapter = FollowerAdapter()
binding.rvFollower.adapter = followerAdapter
}

private fun initLoadingDialogFragment() {
_loadingDialog = LoadingDialogFragment()
}

private fun initRecyclerViewLayoutManager() {
val layoutManager = GridLayoutManager(activity, 2)
layoutManager.spanSizeLookup = object : SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (followerAdapter?.getItemViewType(position)) {
return when (followerAdapter.getItemViewType(position)) {
VIEW_TYPE_HEADER -> 2
else -> 1
}
Expand All @@ -51,16 +65,42 @@ class FollowerFragment : BindingFragment<FragmentFollowerBinding>(R.layout.fragm
private fun setupGetFollowerListState() {
viewModel.getFollowerListState.observe(viewLifecycleOwner) { state ->
when (state) {
is Success -> followerAdapter?.submitList(viewModel.followerList)
is Failure -> requireContext().showSnackbar(binding.root, getString(R.string.follower_get_follower_list_null_msg))
is Error -> requireContext().showSnackbar(binding.root, getString(R.string.unknown_error_msg))
is Loading -> {
loadingDialog.show(parentFragmentManager, TAG_LOADING_DIALOG)
}

is Success -> {

Choose a reason for hiding this comment

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

오,,, 각 상태별로 로직을 처리하니까 가독성이 훨씬 좋네요 👍

dismissLoadingDialog()
followerAdapter.submitList(viewModel.followerList)
}

is Failure -> {
dismissLoadingDialog()
requireContext().showSnackbar(
binding.root,
getString(R.string.follower_get_follower_list_null_msg),
)
}

is Error -> {
dismissLoadingDialog()
requireContext().showSnackbar(
binding.root,
getString(R.string.unknown_error_msg),
)
}
}
}
}

private fun dismissLoadingDialog() {
if (loadingDialog.isAdded) loadingDialog.dismiss()
}

override fun onDestroyView() {
super.onDestroyView()
followerAdapter = null
_followerAdapter = null
_loadingDialog = null
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.android.go.sopt.domain.repository.FollowerRepository
import org.android.go.sopt.util.state.RemoteUiState
import org.android.go.sopt.util.state.RemoteUiState.Error
import org.android.go.sopt.util.state.RemoteUiState.Failure
import org.android.go.sopt.util.state.RemoteUiState.Loading
import org.android.go.sopt.util.state.RemoteUiState.Success
import retrofit2.HttpException
import timber.log.Timber
Expand All @@ -33,8 +34,9 @@ class FollowerViewModel @Inject constructor(
}

private fun getFollowerList() {
Timber.d("get follower list 시작")
viewModelScope.launch {
_getFollowerListState.value = Loading
// TODO: 팔로워 페이징 구현
followerRepository.getFollowerList(1)
.onSuccess { response ->
if (response.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import org.android.go.sopt.R
import org.android.go.sopt.databinding.FragmentGalleryBinding
import org.android.go.sopt.util.binding.BindingFragment


class GalleryFragment : BindingFragment<FragmentGalleryBinding>(R.layout.fragment_gallery) {
private var imageAdapter: ImageAdapter? = null
private var _imageAdapter: ImageAdapter? = null
private val imageAdapter
get() = requireNotNull(_imageAdapter) { getString(R.string.adapter_not_initialized_error_msg) }

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Expand All @@ -17,9 +18,9 @@ class GalleryFragment : BindingFragment<FragmentGalleryBinding>(R.layout.fragmen
}

private fun initImageAdapter() {
imageAdapter = ImageAdapter()
_imageAdapter = ImageAdapter()
binding.vpGallery.adapter = imageAdapter
imageAdapter?.submitList(
imageAdapter.submitList(
listOf(
R.drawable.img_main_profile,
R.drawable.img_main_profile,
Expand All @@ -30,7 +31,7 @@ class GalleryFragment : BindingFragment<FragmentGalleryBinding>(R.layout.fragmen

override fun onDestroyView() {
super.onDestroyView()
imageAdapter = null
_imageAdapter = null
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.android.go.sopt.presentation.main.gallery

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.android.go.sopt.databinding.ItemGalleryImageBinding
Expand All @@ -20,16 +19,10 @@ class ImageAdapter : ListAdapter<Int, RecyclerView.ViewHolder>(diffUtil) {
)
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is ImageViewHolder) holder.setImage(getItem(position))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {}

class ImageViewHolder(private val binding: ItemGalleryImageBinding) :
RecyclerView.ViewHolder(binding.root) {
fun setImage(@DrawableRes index: Int) {
// TODO : 이미지 연결
}
}
RecyclerView.ViewHolder(binding.root)

companion object {
private val diffUtil = DiffCallback<Int>(
Expand Down
Loading