Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/Refactor/#861-NetworkViewModel_적…
Browse files Browse the repository at this point in the history
…용' into Refactor/#861-NetworkViewModel_적용
  • Loading branch information
ki960213 committed Dec 2, 2023
2 parents b7717c8 + 4e2c97a commit 1fe4bf1
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import androidx.fragment.app.viewModels
import com.emmsale.R
import com.emmsale.databinding.FragmentScrappedEventBinding
import com.emmsale.presentation.base.BaseFragment
import com.emmsale.presentation.common.CommonUiEvent
import com.emmsale.presentation.common.ScrollTopListener
import com.emmsale.presentation.common.extension.showToast
import com.emmsale.presentation.ui.eventDetail.EventDetailActivity
import com.emmsale.presentation.ui.scrappedEventList.recyclerView.ScrappedEventAdapter
import com.emmsale.presentation.ui.scrappedEventList.uiState.ScrappedEventUiState
Expand All @@ -23,29 +25,44 @@ class ScrappedEventFragment : BaseFragment<FragmentScrappedEventBinding>() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initBinding()
setUpScrappedEvents()
setupBinding()
observeScrappedEvents()
observeCommonUiEvent()
}

override fun onResume() {
super.onResume()
viewModel.refresh()
if (viewModel.isFirstFetch) {
viewModel.fetchScrappedEvents()
viewModel.isFirstFetch = false
} else {
viewModel.refresh()
}
}

private fun initBinding() {
private fun setupBinding() {
binding.vm = viewModel
binding.rvScrappedEvents.adapter = scrappedEventsAdapter
binding.rvScrappedEvents.addOnScrollListener(
ScrollTopListener(targetView = binding.fabScrollTop),
)
}

private fun setUpScrappedEvents() {
private fun observeScrappedEvents() {
viewModel.scrappedEvents.observe(viewLifecycleOwner) { scrappedEvents ->
scrappedEventsAdapter.submitList(scrappedEvents.list)
}
}

private fun observeCommonUiEvent() {
viewModel.commonUiEvent.observe(viewLifecycleOwner) { commonUiEvent ->
when (commonUiEvent) {
CommonUiEvent.RequestFailByNetworkError -> showToast(getString(R.string.all_network_error_message))
is CommonUiEvent.Unexpected -> showToast(getString(R.string.all_network_error_message))
}
}
}

private fun showEventDetail(scrappedEventUiState: ScrappedEventUiState) {
EventDetailActivity.startActivity(requireContext(), scrappedEventUiState.event.id)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,40 @@
package com.emmsale.presentation.ui.scrappedEventList

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.emmsale.data.common.retrofit.callAdapter.Success
import com.emmsale.data.repository.interfaces.EventRepository
import com.emmsale.presentation.common.FetchResult
import com.emmsale.presentation.base.NetworkViewModel
import com.emmsale.presentation.common.ScreenUiState
import com.emmsale.presentation.common.livedata.NotNullLiveData
import com.emmsale.presentation.common.livedata.NotNullMutableLiveData
import com.emmsale.presentation.common.viewModel.Refreshable
import com.emmsale.presentation.ui.scrappedEventList.uiState.ScrappedEventsUiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import kotlinx.coroutines.Job
import javax.inject.Inject

@HiltViewModel
class ScrappedEventViewModel @Inject constructor(
private val eventRepository: EventRepository,
) : ViewModel(), Refreshable {
) : NetworkViewModel() {
private val _scrappedEvents = NotNullMutableLiveData(ScrappedEventsUiState())
val scrappedEvents: NotNullLiveData<ScrappedEventsUiState> = _scrappedEvents

override fun refresh() {
changeToLoadingState()
viewModelScope.launch {
when (val response = eventRepository.getScrappedEvents()) {
is Success -> _scrappedEvents.value = ScrappedEventsUiState.from(response.data)
else -> changeToErrorState()
}
}
init {
// NoContentView가 활성화 되지 않기위해 필요
_screenUiState.value = ScreenUiState.LOADING
}

private fun changeToLoadingState() {
_scrappedEvents.value = ScrappedEventsUiState(fetchResult = FetchResult.LOADING)
var isFirstFetch: Boolean = true

override fun refresh(): Job {
return refreshData(
refresh = { eventRepository.getScrappedEvents() },
onSuccess = { _scrappedEvents.value = ScrappedEventsUiState.from(it) },
)
}

private fun changeToErrorState() {
_scrappedEvents.value =
ScrappedEventsUiState(fetchResult = FetchResult.ERROR)
fun fetchScrappedEvents() {
fetchData(
fetchData = { eventRepository.getScrappedEvents() },
onSuccess = { _scrappedEvents.value = ScrappedEventsUiState.from(it) },
)
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package com.emmsale.presentation.ui.scrappedEventList.uiState

import com.emmsale.data.model.Event
import com.emmsale.presentation.common.FetchResult
import com.emmsale.presentation.common.FetchResultUiState

data class ScrappedEventsUiState(
val list: List<ScrappedEventUiState> = listOf(),
override val fetchResult: FetchResult = FetchResult.LOADING,
) : FetchResultUiState() {
) {
companion object {
fun from(scrappedEvents: List<Event>): ScrappedEventsUiState =
ScrappedEventsUiState(
list = scrappedEvents.map { ScrappedEventUiState.from(it) },
fetchResult = FetchResult.SUCCESS,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>
Expand All @@ -10,6 +9,8 @@

<import type="com.emmsale.presentation.common.FetchResult" />

<import type="com.emmsale.presentation.common.ScreenUiState" />

<variable
name="vm"
type="com.emmsale.presentation.ui.scrappedEventList.ScrappedEventViewModel" />
Expand All @@ -20,94 +21,108 @@
android:layout_height="match_parent"
tools:background="@color/white">

<TextView
android:id="@+id/tv_scraps_prefix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="17dp"
android:layout_marginTop="@dimen/event_count_text_margin_top"
android:text="@string/event_count_prefix"
android:textColor="@color/black"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
app:onRefresh1="@{()->vm.refresh()}"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:swipeRefreshColor="@{@color/primary_color}">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/tv_events_count"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="6dp"
android:gravity="center"
android:text="@{@string/event_count_format(vm.scrappedEvents.list.size)}"
android:textColor="@color/primary_color"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/tv_scraps_prefix"
app:layout_constraintStart_toEndOf="@+id/tv_scraps_prefix"
app:layout_constraintTop_toTopOf="@+id/tv_scraps_prefix"
tools:text="58" />
<TextView
android:id="@+id/tv_scraps_prefix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="17dp"
android:layout_marginTop="@dimen/event_count_text_margin_top"
android:text="@string/event_count_prefix"
android:textColor="@color/black"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_scrapped_events"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="6dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_events_count"
app:spanCount="@integer/event_column_size"
tools:background="@drawable/bg_event_recyclerview"
tools:itemCount="2"
tools:listitem="@layout/item_conference" />
<TextView
android:id="@+id/tv_events_count"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="6dp"
android:gravity="center"
android:text="@{@string/event_count_format(vm.scrappedEvents.list.size)}"
android:textColor="@color/primary_color"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/tv_scraps_prefix"
app:layout_constraintStart_toEndOf="@+id/tv_scraps_prefix"
app:layout_constraintTop_toTopOf="@+id/tv_scraps_prefix"
tools:text="58" />

<include
layout="@layout/layout_all_error_screen"
android:background="@color/white"
android:clickable="true"
app:visible="@{vm.scrappedEvents.fetchResult == FetchResult.ERROR}"
bind:viewModel="@{vm}"
tools:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_scrapped_events"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="6dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_events_count"
app:spanCount="@integer/event_column_size"
tools:background="@drawable/bg_event_recyclerview"
tools:itemCount="2"
tools:listitem="@layout/item_conference" />

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_scroll_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:src="@drawable/ic_all_arrow_up"
android:visibility="gone"
app:backgroundTint="@color/primary_color"
app:borderWidth="3dp"
app:fabCustomSize="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:maxImageSize="16dp"
app:tint="@color/white"
tools:visibility="visible" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_scroll_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:src="@drawable/ic_all_arrow_up"
android:visibility="gone"
app:backgroundTint="@color/primary_color"
app:borderWidth="3dp"
app:fabCustomSize="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:maxImageSize="16dp"
app:tint="@color/white"
tools:visibility="visible" />

<com.emmsale.presentation.common.views.NoContentView
android:layout_width="0dp"
android:layout_height="0dp"
app:description="@string/scrappedevent_no_content_view_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:noContentImageResId="@drawable/ic_no_result1"
app:title="@string/scrappedevent_no_content_view_desc"
app:visible="@{vm.scrappedEvents.list.empty &amp;&amp; vm.scrappedEvents.fetchResult == FetchResult.SUCCESS}" />
<com.emmsale.presentation.common.views.NoContentView
android:layout_width="0dp"
android:layout_height="0dp"
app:description="@string/scrappedevent_no_content_view_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:noContentImageResId="@drawable/ic_no_result1"
app:title="@string/scrappedevent_no_content_view_desc"
app:visible="@{vm.scrappedEvents.list.empty &amp;&amp; vm.screenUiState == ScreenUiState.NONE}" />

<ProgressBar
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:visible="@{vm.scrappedEvents.fetchResult == FetchResult.LOADING}"
tools:visibility="gone" />
<ProgressBar
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:visible="@{vm.screenUiState == ScreenUiState.LOADING }"
tools:visibility="gone" />

<com.emmsale.presentation.common.views.NetworkErrorView
android:id="@+id/network_error_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:onRefresh="@{()->vm.refresh()}"
app:visible="@{vm.screenUiState == ScreenUiState.NETWORK_ERROR}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

0 comments on commit 1fe4bf1

Please sign in to comment.