Skip to content

Commit

Permalink
Simplify calculating showed month, add animations when changing month (
Browse files Browse the repository at this point in the history
  • Loading branch information
boguszpawlowski authored Nov 15, 2022
1 parent f070b62 commit 9435077
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public fun <T : SelectionState> Calendar(
Box { content(PaddingValues()) }
},
) {

val initialMonth = remember { calendarState.monthState.currentMonth }
val daysOfWeek = remember(firstDayOfWeek) {
DayOfWeek.values().rotateRight(DaysOfWeek - firstDayOfWeek.ordinal)
}
Expand All @@ -189,6 +189,7 @@ public fun <T : SelectionState> Calendar(
monthHeader(calendarState.monthState)
if (horizontalSwipeEnabled) {
MonthPager(
initialMonth = initialMonth,
showAdjacentMonths = showAdjacentMonths,
monthState = calendarState.monthState,
selectionState = calendarState.selectionState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ import dev.chrisbanes.snapper.SnapperLayoutInfo
import dev.chrisbanes.snapper.rememberSnapperFlingBehavior
import io.github.boguszpawlowski.composecalendar.day.DayState
import io.github.boguszpawlowski.composecalendar.header.MonthState
import io.github.boguszpawlowski.composecalendar.pager.PagerItemCount
import io.github.boguszpawlowski.composecalendar.pager.toIndex
import io.github.boguszpawlowski.composecalendar.selection.SelectionState
import io.github.boguszpawlowski.composecalendar.week.WeekContent
import io.github.boguszpawlowski.composecalendar.week.getWeeks
Expand All @@ -35,8 +33,9 @@ internal const val DaysOfWeek = 7

@OptIn(ExperimentalSnapperApi::class)
@Composable
@Suppress("LongMethod")
@Suppress("LongMethod", "LongParameterList")
internal fun <T : SelectionState> MonthPager(
initialMonth: YearMonth,
showAdjacentMonths: Boolean,
selectionState: T,
monthState: MonthState,
Expand All @@ -47,11 +46,10 @@ internal fun <T : SelectionState> MonthPager(
weekHeader: @Composable BoxScope.(List<DayOfWeek>) -> Unit,
monthContainer: @Composable (content: @Composable (PaddingValues) -> Unit) -> Unit,
) {
val startIndex = PagerItemCount / 2
val coroutineScope = rememberCoroutineScope()

val listState = rememberLazyListState(
initialFirstVisibleItemIndex = startIndex,
initialFirstVisibleItemIndex = StartIndex,
)
val flingBehavior = rememberSnapperFlingBehavior(
lazyListState = listState,
Expand All @@ -64,6 +62,7 @@ internal fun <T : SelectionState> MonthPager(
val monthListState = remember {
MonthListState(
coroutineScope = coroutineScope,
initialMonth = initialMonth,
monthState = monthState,
listState = listState,
)
Expand All @@ -80,7 +79,7 @@ internal fun <T : SelectionState> MonthPager(
modifier = Modifier.fillParentMaxWidth(),
showAdjacentMonths = showAdjacentMonths,
selectionState = selectionState,
currentMonth = monthListState.getMonthForPage(index.toIndex()),
currentMonth = monthListState.getMonthForPage(index),
today = today,
daysOfWeek = daysOfWeek,
dayContent = dayContent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,68 +1,49 @@
package io.github.boguszpawlowski.composecalendar.month

import android.annotation.SuppressLint
import androidx.annotation.IntRange
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import io.github.boguszpawlowski.composecalendar.header.MonthState
import io.github.boguszpawlowski.composecalendar.pager.toIndex
import io.github.boguszpawlowski.composecalendar.util.dec
import io.github.boguszpawlowski.composecalendar.util.inc
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.runningFold
import kotlinx.coroutines.launch
import java.time.YearMonth

internal const val PageCount = 3
import java.time.temporal.ChronoUnit

@Stable
internal class MonthListState(
coroutineScope: CoroutineScope,
private val coroutineScope: CoroutineScope,
private val initialMonth: YearMonth,
private val monthState: MonthState,
private val listState: LazyListState,
) {

private var monthProvider by mutableStateOf(
MonthProvider(
initialMonth = monthState.currentMonth,
currentIndex = listState.currentIndex,
)
)
private val currentlyVisibleMonth by derivedStateOf {
getMonthForPage(listState.firstVisibleItemIndex)
}

init {
snapshotFlow { monthState.currentMonth }.onEach { month ->
moveToMonth(month)
}.launchIn(coroutineScope)

snapshotFlow { listState.currentIndex }.runningFold(1 to 1) { oldIndices, newIndex ->
oldIndices.second to newIndex
}.distinctUntilChanged().onEach { (oldIndex, newIndex) ->
onScrolled(oldIndex, newIndex)
monthState.currentMonth = getMonthForPage(newIndex)
snapshotFlow { currentlyVisibleMonth }.onEach { newMonth ->
monthState.currentMonth = newMonth
}.launchIn(coroutineScope)
}

fun getMonthForPage(@IntRange(from = 0, to = 2) index: Int) = monthProvider.cache[index]!!
fun getMonthForPage(index: Int): YearMonth =
initialMonth.plusMonths((index - StartIndex).toLong())

@SuppressLint("Range")
private fun moveToMonth(month: YearMonth) {
if (month - getMonthForPage(listState.currentIndex) == 0) return
monthProvider = MonthProvider(monthState.currentMonth, listState.currentIndex)
}

private fun onScrolled(oldIndex: Int, newIndex: Int) {
when (oldIndex - newIndex) {
-1, PageCount - 1 -> monthProvider.cache[(newIndex + 1) % PageCount] =
monthProvider.cache[newIndex]!!.inc()
else -> monthProvider.cache[(newIndex - 1 + PageCount) % PageCount] =
monthProvider.cache[newIndex]!!.dec()
if (month == currentlyVisibleMonth) return
initialMonth.minus(month).let { offset ->
coroutineScope.launch {
listState.animateScrollToItem((StartIndex - offset).toInt())
}
}
}

Expand All @@ -74,46 +55,19 @@ internal class MonthListState(

if (monthState != other.monthState) return false
if (listState != other.listState) return false
if (monthProvider != other.monthProvider) return false

return true
}

override fun hashCode(): Int {
var result = monthState.hashCode()
result = 31 * result + listState.hashCode()
result = 31 * result + monthProvider.hashCode()
return result
}
}

@Stable
internal class MonthProvider(initialMonth: YearMonth, currentIndex: Int) {
val cache = mutableStateMapOf<Int, YearMonth>()

init {
cache[(currentIndex - 1 + PageCount) % PageCount] = initialMonth.dec()
cache[currentIndex] = initialMonth
cache[(currentIndex + 1) % PageCount] = initialMonth.inc()
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as MonthProvider

if (cache != other.cache) return false

return true
}

override fun hashCode(): Int {
return cache.hashCode()
}
}

private operator fun YearMonth.minus(other: YearMonth) =
year - other.year + month.value - other.month.value
ChronoUnit.MONTHS.between(other, this)

private val LazyListState.currentIndex get() = firstVisibleItemIndex.toIndex()
internal const val PagerItemCount = 20_000
internal const val StartIndex = PagerItemCount / 2

This file was deleted.

This file was deleted.

0 comments on commit 9435077

Please sign in to comment.