Skip to content

Commit

Permalink
Merge branch 'dev' into feat/#750
Browse files Browse the repository at this point in the history
# Conflicts:
#	android/festago/presentation/src/main/res/values/strings.xml
  • Loading branch information
SeongHoonC committed Mar 6, 2024
2 parents 6a7034e + ee2cfc9 commit e2dc49b
Show file tree
Hide file tree
Showing 56 changed files with 1,031 additions and 166 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import javax.inject.Inject
class FakeFestivalRepository @Inject constructor() : FestivalRepository {

override suspend fun loadPopularFestivals(): Result<PopularFestivals> {
return Result.success(PopularFestivals("인기 축제 목록", FakeFestivals.popularFestivals))
return Result.success(
PopularFestivals(
title = "인기 축제 목록",
festivals = FakeFestivals.popularFestivals,
),
)
}

override suspend fun loadFestivals(
Expand All @@ -28,18 +33,24 @@ class FakeFestivalRepository @Inject constructor() : FestivalRepository {
val notNullSize = size ?: DEFAULT_SIZE
val notNullLastFestivalId = lastFestivalId ?: DEFAULT_LAST_FESTIVAL_ID

if (festivalFilter == FestivalFilter.PLANNED) {
return Result.success(
FestivalsPage(isLastPage = true, festivals = FakeFestivals.plannedFestivals),
)
}

if (notNullLastFestivalId + notNullSize < LAST_ITEM_ID) {
return Result.success(
FestivalsPage(
false,
getFestivals((notNullLastFestivalId + 1)..(notNullLastFestivalId + notNullSize)),
isLastPage = false,
festivals = getFestivals((notNullLastFestivalId + 1)..(notNullLastFestivalId + notNullSize)),
),
)
}
return Result.success(
FestivalsPage(
true,
getFestivals((notNullLastFestivalId + 1)..LAST_ITEM_ID),
isLastPage = true,
festivals = getFestivals((notNullLastFestivalId + 1)..LAST_ITEM_ID),
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ object FakeFestivals {

val plannedFestivals = listOf(
Festival(
id = 1,
id = 30,
name = "뉴진스 콘서트",
startDate = LocalDate.now().plusDays(1L),
endDate = LocalDate.MAX,
Expand All @@ -286,7 +286,7 @@ object FakeFestivals {
),
),
Festival(
id = 2,
id = 31,
name = "아이브 콘서트",
startDate = LocalDate.now().plusDays(3L),
endDate = LocalDate.MAX,
Expand Down Expand Up @@ -321,7 +321,7 @@ object FakeFestivals {
),
),
Festival(
id = 3,
id = 32,
name = "아이들 콘서트",
startDate = LocalDate.now().plusDays(5L),
endDate = LocalDate.MAX,
Expand All @@ -336,7 +336,7 @@ object FakeFestivals {
),
),
Festival(
id = 4,
id = 33,
name = "뉴진스 콘서트",
startDate = LocalDate.now().plusDays(20L),
endDate = LocalDate.MAX,
Expand All @@ -351,7 +351,7 @@ object FakeFestivals {
),
),
Festival(
id = 5,
id = 34,
name = "아이브 콘서트",
startDate = LocalDate.now().plusDays(40L),
endDate = LocalDate.MAX,
Expand Down
5 changes: 4 additions & 1 deletion android/festago/presentation/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application>
<activity
android:name=".ui.notificationlist.NotificationListActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".ui.home.HomeActivity"
android:exported="true"
Expand All @@ -13,5 +17,4 @@
</activity>
</application>


</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.festago.festago.presentation.ui.customview

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.recyclerview.widget.RecyclerView

/**
* A RecyclerView that only handles scroll events with the same orientation of its LayoutManager.
* Avoids situations where nested recyclerviews don't receive touch events properly:
*/
class OrientationAwareRecyclerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet,
defStyleAttr: Int = 0,
) : RecyclerView(context, attrs, defStyleAttr) {

private var lastX = 0.0f
private var lastY = 0.0f
private var scrolling = false

init {
addOnScrollListener(object : OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
scrolling = newState != SCROLL_STATE_IDLE
}
})
}

override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
val lm = layoutManager ?: return super.onInterceptTouchEvent(e)
var allowScroll = true
when (e.actionMasked) {
MotionEvent.ACTION_DOWN -> {
lastX = e.x
lastY = e.y
// If we were scrolling, stop now by faking a touch release
if (scrolling) {
val newEvent = MotionEvent.obtain(e)
newEvent.action = MotionEvent.ACTION_UP
return super.onInterceptTouchEvent(newEvent)
}
}

MotionEvent.ACTION_MOVE -> {
// We're moving, so check if we're trying
// to scroll vertically or horizontally so we don't intercept the wrong event.
val currentX = e.x
val currentY = e.y
val dx = Math.abs(currentX - lastX)
val dy = Math.abs(currentY - lastY)
allowScroll = if (dy > dx) lm.canScrollVertically() else lm.canScrollHorizontally()
}
}
if (!allowScroll) return false
return super.onInterceptTouchEvent(e)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.festago.festago.presentation.R
import com.festago.festago.presentation.databinding.FragmentFestivalListBinding
import com.festago.festago.presentation.ui.artistdetail.ArtistDetailFragment
import com.festago.festago.presentation.ui.festivaldetail.FestivalDetailFragment
import com.festago.festago.presentation.ui.home.festivallist.festival.FestivalListAdapter
import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalFilterUiState
import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalListUiState
import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalMoreItemUiState
import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalTabUiState
import com.festago.festago.presentation.ui.notificationlist.NotificationListActivity
import com.festago.festago.presentation.ui.schooldetail.SchoolDetailFragment
import com.festago.festago.presentation.util.repeatOnStarted
import com.festago.festago.presentation.util.setOnApplyWindowInsetsCompatListener
Expand Down Expand Up @@ -72,20 +74,24 @@ class FestivalListFragment : Fragment() {
}

private fun initView() {
vm.loadFestivals()
vm.initFestivalList()
initViewPager()
initRecyclerView()
initRefresh()
}

private fun initRefresh() {
binding.srlFestivalList.setOnRefreshListener {
vm.loadFestivals()
vm.initFestivalList()
binding.srlFestivalList.isRefreshing = false
}
binding.srlFestivalList.setDistanceToTriggerSync(400)
binding.ivSearch.setOnClickListener { // 임시 연결
showSchoolDetail()
}
binding.ivAlarm.setOnClickListener {
showNotificationList()
}
}

private fun initViewPager() {
Expand All @@ -103,6 +109,30 @@ class FestivalListFragment : Fragment() {
}

private fun initRecyclerView() {
initScrollEvent()
initDecoration()
}

private fun initScrollEvent() {
binding.rvFestivalList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)

val festivalListUiState = vm.uiState.value as? FestivalListUiState.Success ?: return
if (festivalListUiState.isLastPage) return

val lastVisibleItemPosition =
(recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition()

val itemTotalCount = recyclerView.adapter!!.itemCount - 1
if (lastVisibleItemPosition == itemTotalCount) {
vm.loadFestivals()
}
}
})
}

private fun initDecoration() {
binding.rvFestivalList.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
Expand Down Expand Up @@ -148,19 +178,19 @@ class FestivalListFragment : Fragment() {
}

private fun handleSuccess(uiState: FestivalListUiState.Success) {
festivalListAdapter.submitList(
listOf(
uiState.popularFestivals,
FestivalTabUiState {
val festivalFilter = when (it) {
0 -> FestivalFilterUiState.PROGRESS
1 -> FestivalFilterUiState.PLANNED
else -> FestivalFilterUiState.PROGRESS
}
vm.loadFestivals(festivalFilter)
},
) + uiState.festivals,
)
val items = uiState.getItems()
festivalListAdapter.submitList(items)
}

private fun FestivalListUiState.Success.getItems(): List<Any> {
return mutableListOf<Any>().apply {
if (popularFestivalUiState.festivals.isNotEmpty()) {
add(popularFestivalUiState)
}
add(FestivalTabUiState(festivalFilter) { vm.loadFestivals(it) })
addAll(festivals)
if (!isLastPage) add(FestivalMoreItemUiState)
}.toList()
}

private fun showSchoolDetail() {
Expand All @@ -170,6 +200,10 @@ class FestivalListFragment : Fragment() {
.commit()
}

private fun showNotificationList() {
startActivity(NotificationListActivity.getIntent(requireContext()))
}

override fun onDestroyView() {
_binding = null
super.onDestroyView()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,23 @@ class FestivalListViewModel @Inject constructor(

private var festivalFilter: FestivalFilter = FestivalFilter.PROGRESS

fun loadFestivals(festivalFilterUiState: FestivalFilterUiState? = null) {
fun initFestivalList() {
viewModelScope.launch {
if (festivalFilterUiState != null) {
festivalFilter = festivalFilterUiState.toDomain()
}

val deferredPopularFestivals = async { festivalRepository.loadPopularFestivals() }
val deferredFestivals =
async { festivalRepository.loadFestivals(festivalFilter = festivalFilter) }

val deferredFestivals = async {
festivalRepository.loadFestivals(festivalFilter = festivalFilter)
}
runCatching {
val festivalsPage = deferredFestivals.await().getOrThrow()
val popularFestivals = deferredPopularFestivals.await().getOrThrow()

_uiState.value = FestivalListUiState.Success(
PopularFestivalUiState(
title = popularFestivals.title,
popularFestivals = popularFestivals.festivals.map { it.toUiState() },
festivals = popularFestivals.festivals.map { it.toUiState() },
),
festivals = festivalsPage.festivals.map { it.toUiState() },
festivalFilter = festivalFilter.toUiState(),
isLastPage = festivalsPage.isLastPage,
)
}.onFailure {
Expand All @@ -70,11 +67,51 @@ class FestivalListViewModel @Inject constructor(
}
}

fun loadFestivals(festivalFilterUiState: FestivalFilterUiState? = null) {
val successUiState = uiState.value as? FestivalListUiState.Success ?: return

viewModelScope.launch {
val currentFestivals = getCurrentFestivals(festivalFilterUiState)

festivalRepository.loadFestivals(
festivalFilter = festivalFilter,
lastFestivalId = currentFestivals.lastOrNull()?.id,
lastStartDate = currentFestivals.lastOrNull()?.startDate,
).onSuccess { festivalsPage ->
_uiState.value = FestivalListUiState.Success(
PopularFestivalUiState(
title = successUiState.popularFestivalUiState.title,
festivals = successUiState.popularFestivalUiState.festivals,
),
festivals = currentFestivals + festivalsPage.festivals.map { it.toUiState() },
festivalFilter = festivalFilter.toUiState(),
isLastPage = festivalsPage.isLastPage,
)
}
}
}

private fun getCurrentFestivals(festivalFilterUiState: FestivalFilterUiState?): List<FestivalItemUiState> {
var festivals = (uiState.value as? FestivalListUiState.Success)?.festivals ?: listOf()

if (festivalFilterUiState != null && festivalFilter != festivalFilterUiState.toDomain()) {
festivalFilter = festivalFilterUiState.toDomain()
festivals = listOf()
}
return festivals
}

private fun FestivalFilterUiState.toDomain() = when (this) {
FestivalFilterUiState.PLANNED -> FestivalFilter.PLANNED
FestivalFilterUiState.PROGRESS -> FestivalFilter.PROGRESS
}

private fun FestivalFilter.toUiState() = when (this) {
FestivalFilter.PLANNED -> FestivalFilterUiState.PLANNED
FestivalFilter.PROGRESS -> FestivalFilterUiState.PROGRESS
else -> FestivalFilterUiState.PLANNED
}

private fun Festival.toUiState() = FestivalItemUiState(
id = id,
name = name,
Expand Down
Loading

0 comments on commit e2dc49b

Please sign in to comment.