Skip to content

Commit

Permalink
[FEAT/#8] get follower list 서버 통신 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
b1urrrr committed May 12, 2023
1 parent 386777b commit 5e9e42a
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ data class ResponseGetFollowerListDto(

fun toFollower() = data.map { follower ->
Follower(
id = follower.id,
name = "${follower.firstName} ${follower.lastName}",
profile = follower.avatar,
email = follower.email,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import retrofit2.http.Query

interface FollowerService {
@GET("users")
fun getFollowerList(
suspend fun getFollowerList(
@Query("page") page: Int,
): ResponseGetFollowerListDto
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import javax.inject.Inject
class FollowerDataSource @Inject constructor(
private val followerService: FollowerService,
) {
fun getFollowerList(page: Int): ResponseGetFollowerListDto =
suspend fun getFollowerList(page: Int): ResponseGetFollowerListDto =
followerService.getFollowerList(page)
}
8 changes: 8 additions & 0 deletions app/src/main/java/org/android/go/sopt/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.android.go.sopt.data.repository.AuthRepositoryImpl
import org.android.go.sopt.data.repository.FollowerRepositoryImpl
import org.android.go.sopt.data.repository.RepoRepositoryImpl
import org.android.go.sopt.domain.repository.AuthRepository
import org.android.go.sopt.domain.repository.FollowerRepository
import org.android.go.sopt.domain.repository.RepoRepository
import javax.inject.Singleton

Expand All @@ -24,4 +26,10 @@ abstract class RepositoryModule {
abstract fun bindsRepoRepository(
repoRepositoryImpl: RepoRepositoryImpl,
): RepoRepository

@Binds
@Singleton
abstract fun bindsFollowerRepository(
followerRepositoryImpl: FollowerRepositoryImpl,
): FollowerRepository
}
6 changes: 4 additions & 2 deletions app/src/main/java/org/android/go/sopt/di/ServiceModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.android.go.sopt.data.service.AuthService
import org.android.go.sopt.data.service.FollowerService
import org.android.go.sopt.di.RetrofitModule.Retrofit2
import org.android.go.sopt.util.type.BaseUrlType
import retrofit2.Retrofit
import javax.inject.Singleton

Expand All @@ -14,11 +16,11 @@ import javax.inject.Singleton
object ServiceModule {
@Provides
@Singleton
fun providesAuthService(retrofit: Retrofit): AuthService =
fun providesAuthService(@Retrofit2(BaseUrlType.SOPT) retrofit: Retrofit): AuthService =
retrofit.create(AuthService::class.java)

@Provides
@Singleton
fun providesFollowerService(retrofit: Retrofit): FollowerService =
fun providesFollowerService(@Retrofit2(BaseUrlType.REQRES) retrofit: Retrofit): FollowerService =
retrofit.create(FollowerService::class.java)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.android.go.sopt.domain.model

data class Follower(
val id: Int,
val name: String,
val profile: String,
val email: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.android.go.sopt.presentation.main.follower

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.android.go.sopt.R
import org.android.go.sopt.databinding.HeaderFollowerBinding
import org.android.go.sopt.databinding.ItemFollowerBinding
import org.android.go.sopt.domain.model.Follower
import org.android.go.sopt.util.DiffCallback

class FollowerAdapter : ListAdapter<Follower, RecyclerView.ViewHolder>(diffUtil) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
VIEW_TYPE_HEADER -> FollowerHeaderViewHolder(
HeaderFollowerBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false,
),
)

VIEW_TYPE_ITEM -> FollowerViewHolder(
ItemFollowerBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false,
),
)

else -> throw ClassCastException(
parent.context.getString(
R.string.view_type_error_msg,
viewType,
),
)
}
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is FollowerViewHolder) holder.setFollower(getItem(position - HEADER_COUNT))
}

override fun getItemCount(): Int {
return super.getItemCount() + HEADER_COUNT
}

override fun getItemViewType(position: Int): Int {
return when (position) {
0 -> VIEW_TYPE_HEADER
else -> VIEW_TYPE_ITEM
}
}

class FollowerHeaderViewHolder(private val binding: HeaderFollowerBinding) :
RecyclerView.ViewHolder(binding.root)

class FollowerViewHolder(private val binding: ItemFollowerBinding) :
RecyclerView.ViewHolder(binding.root) {
fun setFollower(follower: Follower) {
binding.data = follower
}
}

companion object {
private val diffUtil = DiffCallback<Follower>(
onItemsTheSame = { old, new -> old.id == new.id },
onContentsTheSame = { old, new -> old == new },
)

private const val HEADER_COUNT = 1

const val VIEW_TYPE_HEADER = 0
const val VIEW_TYPE_ITEM = 1
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,66 @@
package org.android.go.sopt.presentation.main.follower

import android.os.Bundle
import android.view.View
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.GridLayoutManager
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.main.follower.FollowerAdapter.Companion.VIEW_TYPE_HEADER
import org.android.go.sopt.util.binding.BindingFragment
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.Success

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

private var followerAdapter: FollowerAdapter? = null

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

initFollowerAdapter()
initRecyclerViewLayoutManager()
setupGetFollowerListState()
}

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

private fun initRecyclerViewLayoutManager() {
val layoutManager = GridLayoutManager(activity, 2)
layoutManager.spanSizeLookup = object : SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (followerAdapter?.getItemViewType(position)) {
VIEW_TYPE_HEADER -> 2
else -> 1
}
}
}
binding.rvFollower.layoutManager = layoutManager
}

private fun setupGetFollowerListState() {
viewModel.getFollowerListState.observe(viewLifecycleOwner) { state ->
when (state) {
is Success -> followerAdapter?.submitList(viewModel.followerList)
is Failure -> {}
is Error -> {}
}
}
}

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

companion object {
@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.android.go.sopt.presentation.main.follower

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import org.android.go.sopt.domain.model.Follower
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.Success
import retrofit2.HttpException
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
class FollowerViewModel @Inject constructor(
private val followerRepository: FollowerRepository,
) : ViewModel() {
private val _followerList = MutableLiveData<List<Follower>>()
val followerList: List<Follower>?
get() = _followerList.value

private val _getFollowerListState = MutableLiveData<RemoteUiState>()
val getFollowerListState: LiveData<RemoteUiState>
get() = _getFollowerListState

init {
getFollowerList()
}

private fun getFollowerList() {
Timber.d("get follower list 시작")
viewModelScope.launch {
followerRepository.getFollowerList(1)
.onSuccess { response ->
// TODO: followerList null 처리
_followerList.value = response
_getFollowerListState.value = Success
Timber.d("GET FOLLOWER LIST SUCCESS : $response")
}
.onFailure { t ->
if (t is HttpException) {
// TODO : failure 예외 처리
_getFollowerListState.value = Error
Timber.e("GET FOLLOWER LIST FAIL : ${t.message}")
}
}
}
}
}
9 changes: 6 additions & 3 deletions app/src/main/res/layout/fragment_follower.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

<data>

<variable
name="vm"
type="org.android.go.sopt.presentation.main.follower.FollowerViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
Expand All @@ -19,13 +22,13 @@
android:layout_height="0dp"
android:clipToPadding="false"
android:paddingVertical="10dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:spanCount="2"
tools:listitem="@layout/item_follower" />
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="@layout/item_follower"
tools:spanCount="2" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
12 changes: 9 additions & 3 deletions app/src/main/res/layout/item_follower.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@

<data>

<variable
name="data"
type="org.android.go.sopt.domain.model.Follower" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_item_follower"
android:layout_width="wrap_content"
android:layout_marginTop="30dp"
android:layout_height="wrap_content">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp">

<ImageView
android:id="@+id/iv_follower_profile"
setCircleImage="@{data.profile}"
android:layout_width="100dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1"
Expand All @@ -30,6 +34,7 @@
android:layout_marginTop="10dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@{data.name}"
android:textAppearance="?textAppearanceBodyLarge"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
Expand All @@ -43,6 +48,7 @@
android:layout_marginTop="6dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@{data.email}"
android:textAppearance="?textAppearanceBodySmall"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
Expand Down

0 comments on commit 5e9e42a

Please sign in to comment.