Page the week, multi-day and day timelines with one DaysGrid
This commit is contained in:
@@ -7,100 +7,63 @@ import kotlinx.datetime.LocalDate
|
|||||||
import kotlinx.datetime.daysUntil
|
import kotlinx.datetime.daysUntil
|
||||||
import kotlinx.datetime.plus
|
import kotlinx.datetime.plus
|
||||||
|
|
||||||
/** Pages the week pager spans — a little under four centuries, centred on [PAGE_EPOCH]. */
|
/** Pages the week pager spans — a little under four centuries. */
|
||||||
internal const val WEEK_PAGE_COUNT: Int = 20_000
|
internal const val WEEK_PAGE_COUNT: Int = 20_000
|
||||||
|
|
||||||
private const val EPOCH_PAGE = WEEK_PAGE_COUNT / 2
|
|
||||||
|
|
||||||
/** The week at [EPOCH_PAGE]; any date works, it only has to stay fixed. */
|
|
||||||
private val PAGE_EPOCH = LocalDate(2000, 1, 3)
|
|
||||||
|
|
||||||
/** First day of the week shown on pager [page], with weeks starting on [firstDay]. */
|
|
||||||
internal fun weekStartForPage(page: Int, firstDay: DayOfWeek): LocalDate =
|
|
||||||
PAGE_EPOCH.startOfWeek(firstDay).plus((page - EPOCH_PAGE) * 7, DateTimeUnit.DAY)
|
|
||||||
|
|
||||||
/** The pager page whose week contains [date], with weeks starting on [firstDay]. */
|
|
||||||
internal fun weekPageFor(date: LocalDate, firstDay: DayOfWeek): Int {
|
|
||||||
val days = PAGE_EPOCH.startOfWeek(firstDay).daysUntil(date.startOfWeek(firstDay))
|
|
||||||
return (EPOCH_PAGE + days / 7).coerceIn(0, WEEK_PAGE_COUNT - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** How a timeline pager cuts the calendar into pages of [dayCount] days. */
|
|
||||||
sealed interface PageGrid {
|
|
||||||
val dayCount: Int
|
|
||||||
val pageCount: Int
|
|
||||||
|
|
||||||
/** First day shown on pager [page]. */
|
|
||||||
fun startForPage(page: Int): LocalDate
|
|
||||||
|
|
||||||
/** The pager page showing [date]. */
|
|
||||||
fun pageFor(date: LocalDate): Int
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Whole weeks, starting on [firstDay]. */
|
|
||||||
data class WeekGrid(val firstDay: DayOfWeek) : PageGrid {
|
|
||||||
override val dayCount: Int get() = 7
|
|
||||||
override val pageCount: Int get() = WEEK_PAGE_COUNT
|
|
||||||
override fun startForPage(page: Int): LocalDate = weekStartForPage(page, firstDay)
|
|
||||||
override fun pageFor(date: LocalDate): Int = weekPageFor(date, firstDay)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Pages the multi-day pager spans — over three centuries at the widest page. */
|
/** Pages the multi-day pager spans — over three centuries at the widest page. */
|
||||||
internal const val DAYS_PAGE_COUNT: Int = 40_000
|
internal const val DAYS_PAGE_COUNT: Int = 40_000
|
||||||
|
|
||||||
private const val DAYS_EPOCH_PAGE = DAYS_PAGE_COUNT / 2
|
/** Pages the day pager spans — a little under four centuries. */
|
||||||
|
internal const val DAY_PAGE_COUNT: Int = 140_000
|
||||||
|
|
||||||
/** A fixed day the multi-day pages are counted from. */
|
/** A fixed day every grid's pages are counted from, at its middle page. */
|
||||||
private val DAYS_EPOCH = LocalDate(2000, 1, 1)
|
private val DAYS_EPOCH = LocalDate(2000, 1, 1)
|
||||||
|
|
||||||
|
/** The week the week pager's middle page shows; any date works, it only has to stay fixed. */
|
||||||
|
private val WEEK_EPOCH = LocalDate(2000, 1, 3)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs of [dayCount] days (#193), offset [phase] days from a fixed epoch so any
|
* How a timeline pager cuts the calendar: runs of [dayCount] days, offset
|
||||||
* date can start a page. The phase stays put while paging and only changes when
|
* [phase] days from a fixed epoch, over [pageCount] pages centred on it. The
|
||||||
* a jump moves another date to the first column.
|
* multi-day view's phase (#193) stays put while paging and only changes when a
|
||||||
|
* jump moves another date to the first column.
|
||||||
*/
|
*/
|
||||||
data class DaysGrid(override val dayCount: Int, val phase: Int) : PageGrid {
|
data class DaysGrid(val dayCount: Int, val phase: Int, val pageCount: Int = DAYS_PAGE_COUNT) {
|
||||||
override val pageCount: Int get() = DAYS_PAGE_COUNT
|
|
||||||
|
|
||||||
override fun startForPage(page: Int): LocalDate =
|
private val epochPage: Int get() = pageCount / 2
|
||||||
DAYS_EPOCH.plus(phase + (page - DAYS_EPOCH_PAGE) * dayCount, DateTimeUnit.DAY)
|
|
||||||
|
|
||||||
override fun pageFor(date: LocalDate): Int =
|
/** First day shown on pager [page]. */
|
||||||
(DAYS_EPOCH_PAGE + Math.floorDiv(DAYS_EPOCH.daysUntil(date) - phase, dayCount))
|
fun startForPage(page: Int): LocalDate =
|
||||||
.coerceIn(0, DAYS_PAGE_COUNT - 1)
|
DAYS_EPOCH.plus(phase + (page - epochPage) * dayCount, DateTimeUnit.DAY)
|
||||||
|
|
||||||
|
/** The pager page showing [date]. */
|
||||||
|
fun pageFor(date: LocalDate): Int =
|
||||||
|
(epochPage + Math.floorDiv(DAYS_EPOCH.daysUntil(date) - phase, dayCount))
|
||||||
|
.coerceIn(0, pageCount - 1)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/** Single days: every date starts a page of its own. */
|
||||||
|
val days: DaysGrid = DaysGrid(dayCount = 1, phase = 0, pageCount = DAY_PAGE_COUNT)
|
||||||
|
|
||||||
|
/** Whole weeks, starting on [firstDay]. */
|
||||||
|
fun weeks(firstDay: DayOfWeek): DaysGrid = DaysGrid(
|
||||||
|
dayCount = 7,
|
||||||
|
phase = DAYS_EPOCH.daysUntil(WEEK_EPOCH.startOfWeek(firstDay)),
|
||||||
|
pageCount = WEEK_PAGE_COUNT,
|
||||||
|
)
|
||||||
|
|
||||||
/** The grid of [dayCount]-day pages on which [date] is a page's first day. */
|
/** The grid of [dayCount]-day pages on which [date] is a page's first day. */
|
||||||
fun startingAt(date: LocalDate, dayCount: Int): DaysGrid =
|
fun startingAt(date: LocalDate, dayCount: Int): DaysGrid =
|
||||||
DaysGrid(dayCount, Math.floorMod(DAYS_EPOCH.daysUntil(date), dayCount))
|
DaysGrid(dayCount, Math.floorMod(DAYS_EPOCH.daysUntil(date), dayCount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Pages the day pager spans — a little under four centuries, centred on [DAY_EPOCH]. */
|
|
||||||
internal const val DAY_PAGE_COUNT: Int = 140_000
|
|
||||||
|
|
||||||
private const val DAY_EPOCH_PAGE = DAY_PAGE_COUNT / 2
|
|
||||||
|
|
||||||
/** The day at [DAY_EPOCH_PAGE]; any date works, it only has to stay fixed. */
|
|
||||||
private val DAY_EPOCH = LocalDate(2000, 1, 1)
|
|
||||||
|
|
||||||
/** Single days: every date starts a page of its own. */
|
|
||||||
data object DayGrid : PageGrid {
|
|
||||||
override val dayCount: Int get() = 1
|
|
||||||
override val pageCount: Int get() = DAY_PAGE_COUNT
|
|
||||||
|
|
||||||
override fun startForPage(page: Int): LocalDate =
|
|
||||||
DAY_EPOCH.plus(page - DAY_EPOCH_PAGE, DateTimeUnit.DAY)
|
|
||||||
|
|
||||||
override fun pageFor(date: LocalDate): Int =
|
|
||||||
(DAY_EPOCH_PAGE + DAY_EPOCH.daysUntil(date)).coerceIn(0, DAY_PAGE_COUNT - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where the focused date goes when the pager settles on [page] (#184): kept if
|
* Where the focused date goes when the pager settles on [page] (#184): kept if
|
||||||
* the page still shows it, otherwise moved by the pages travelled, so it keeps
|
* the page still shows it, otherwise moved by the pages travelled, so it keeps
|
||||||
* its place in the page — the same weekday a week on.
|
* its place in the page — the same weekday a week on.
|
||||||
*/
|
*/
|
||||||
internal fun PageGrid.focusOnPage(focus: LocalDate, page: Int): LocalDate {
|
internal fun DaysGrid.focusOnPage(focus: LocalDate, page: Int): LocalDate {
|
||||||
val start = startForPage(page)
|
val start = startForPage(page)
|
||||||
val end = start.plus(dayCount - 1, DateTimeUnit.DAY)
|
val end = start.plus(dayCount - 1, DateTimeUnit.DAY)
|
||||||
if (focus in start..end) return focus
|
if (focus in start..end) return focus
|
||||||
|
|||||||
@@ -464,11 +464,12 @@ private fun PagedTimelineScreen(
|
|||||||
|
|
||||||
// Straight to the pager: a tap mid-fling must still land, and the anchor
|
// Straight to the pager: a tap mid-fling must still land, and the anchor
|
||||||
// hasn't caught up with the fling yet to tell the difference. A date that
|
// hasn't caught up with the fling yet to tell the difference. A date that
|
||||||
// would open mid-page on this grid re-frames the pages around it instead.
|
// would open mid-page on this grid, in a view that frames its pages from the
|
||||||
|
// date (multi-day), re-frames the pages around it instead.
|
||||||
val jumpToDate: (LocalDate) -> Unit = { target ->
|
val jumpToDate: (LocalDate) -> Unit = { target ->
|
||||||
focus?.date = target
|
focus?.date = target
|
||||||
val page = grid.pageFor(target)
|
val page = grid.pageFor(target)
|
||||||
if (grid is DaysGrid && grid.startForPage(page) != target) {
|
if (grid.startForPage(page) != target && viewModel.rule.value(target) != grid) {
|
||||||
viewModel.jumpTo(target)
|
viewModel.jumpTo(target)
|
||||||
} else {
|
} else {
|
||||||
scope.launch { pagerState.jumpToPage(page, reduceMotion, pageSpec) }
|
scope.launch { pagerState.jumpToPage(page, reduceMotion, pageSpec) }
|
||||||
@@ -531,7 +532,7 @@ private fun WeekContent(
|
|||||||
state: WeekUiState,
|
state: WeekUiState,
|
||||||
today: LocalDate,
|
today: LocalDate,
|
||||||
pagerState: PagerState,
|
pagerState: PagerState,
|
||||||
grid: PageGrid,
|
grid: DaysGrid,
|
||||||
kind: TimelineKind,
|
kind: TimelineKind,
|
||||||
morph: TimelineMorph?,
|
morph: TimelineMorph?,
|
||||||
handoffStrip: Dp?,
|
handoffStrip: Dp?,
|
||||||
@@ -696,7 +697,7 @@ private fun WeekContent(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun WeekPager(
|
private fun WeekPager(
|
||||||
pagerState: PagerState,
|
pagerState: PagerState,
|
||||||
grid: PageGrid,
|
grid: DaysGrid,
|
||||||
kind: TimelineKind,
|
kind: TimelineKind,
|
||||||
span: (DaySpan) -> StateFlow<WeekUiState>,
|
span: (DaySpan) -> StateFlow<WeekUiState>,
|
||||||
today: LocalDate,
|
today: LocalDate,
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ private const val SPAN_CACHE_DAYS = 28
|
|||||||
data class DaySpan(val start: LocalDate, val count: Int)
|
data class DaySpan(val start: LocalDate, val count: Int)
|
||||||
|
|
||||||
/** How the pager cuts the calendar, and the page the anchor sits on. */
|
/** How the pager cuts the calendar, and the page the anchor sits on. */
|
||||||
data class PagerPosition(val grid: PageGrid, val page: Int)
|
data class PagerPosition(val grid: DaysGrid, val page: Int)
|
||||||
|
|
||||||
/** Where [anchor] sits under [rule]. */
|
/** Where [anchor] sits under [rule]. */
|
||||||
fun positionOf(anchor: LocalDate, rule: (LocalDate) -> PageGrid): PagerPosition =
|
fun positionOf(anchor: LocalDate, rule: (LocalDate) -> DaysGrid): PagerPosition =
|
||||||
rule(anchor).let { PagerPosition(it, it.pageFor(anchor)) }
|
rule(anchor).let { PagerPosition(it, it.pageFor(anchor)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,8 +84,8 @@ abstract class PagedTimelineViewModel(
|
|||||||
todaySource: TodaySource,
|
todaySource: TodaySource,
|
||||||
zoneSource: ZoneSource,
|
zoneSource: ZoneSource,
|
||||||
private val io: CoroutineDispatcher,
|
private val io: CoroutineDispatcher,
|
||||||
gridRule: Flow<(LocalDate) -> PageGrid>,
|
gridRule: Flow<(LocalDate) -> DaysGrid>,
|
||||||
initialRule: (LocalDate) -> PageGrid,
|
initialRule: (LocalDate) -> DaysGrid,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +121,7 @@ abstract class PagedTimelineViewModel(
|
|||||||
* The grid for a given anchor, held eagerly so a screen can place its pager
|
* The grid for a given anchor, held eagerly so a screen can place its pager
|
||||||
* on its very first frame rather than a frame after the anchor moved.
|
* on its very first frame rather than a frame after the anchor moved.
|
||||||
*/
|
*/
|
||||||
val rule: StateFlow<(LocalDate) -> PageGrid> =
|
val rule: StateFlow<(LocalDate) -> DaysGrid> =
|
||||||
gridRule.stateIn(viewModelScope, SharingStarted.Eagerly, initialRule)
|
gridRule.stateIn(viewModelScope, SharingStarted.Eagerly, initialRule)
|
||||||
|
|
||||||
/** Today, kept current across midnight so a page left open doesn't mark yesterday (#373). */
|
/** Today, kept current across midnight so a page left open doesn't mark yesterday (#373). */
|
||||||
@@ -233,11 +233,11 @@ class WeekViewModel @Inject constructor(
|
|||||||
zoneSource = zoneSource,
|
zoneSource = zoneSource,
|
||||||
io = io,
|
io = io,
|
||||||
gridRule = settingsPrefs.weekStart.map { pref ->
|
gridRule = settingsPrefs.weekStart.map { pref ->
|
||||||
val grid = WeekGrid(pref.resolveFirstDay(Locale.getDefault()))
|
val grid = DaysGrid.weeks(pref.resolveFirstDay(Locale.getDefault()))
|
||||||
val rule: (LocalDate) -> PageGrid = { grid }
|
val rule: (LocalDate) -> DaysGrid = { grid }
|
||||||
rule
|
rule
|
||||||
},
|
},
|
||||||
initialRule = { WeekGrid(DayOfWeek.MONDAY) },
|
initialRule = { DaysGrid.weeks(DayOfWeek.MONDAY) },
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,8 +278,8 @@ class DayViewModel @Inject constructor(
|
|||||||
todaySource = todaySource,
|
todaySource = todaySource,
|
||||||
zoneSource = zoneSource,
|
zoneSource = zoneSource,
|
||||||
io = io,
|
io = io,
|
||||||
gridRule = flowOf { _: LocalDate -> DayGrid },
|
gridRule = flowOf { _: LocalDate -> DaysGrid.days },
|
||||||
initialRule = { DayGrid },
|
initialRule = { DaysGrid.days },
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ class FocusOnPageTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `a page that still shows the focus keeps it`() {
|
fun `a page that still shows the focus keeps it`() {
|
||||||
val grid = WeekGrid(DayOfWeek.MONDAY)
|
val grid = DaysGrid.weeks(DayOfWeek.MONDAY)
|
||||||
assertThat(grid.focusOnPage(wednesday, grid.pageFor(wednesday))).isEqualTo(wednesday)
|
assertThat(grid.focusOnPage(wednesday, grid.pageFor(wednesday))).isEqualTo(wednesday)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `a week on keeps the weekday`() {
|
fun `a week on keeps the weekday`() {
|
||||||
val grid = WeekGrid(DayOfWeek.MONDAY)
|
val grid = DaysGrid.weeks(DayOfWeek.MONDAY)
|
||||||
val page = grid.pageFor(wednesday)
|
val page = grid.pageFor(wednesday)
|
||||||
assertThat(grid.focusOnPage(wednesday, page + 1)).isEqualTo(LocalDate(2026, 10, 7))
|
assertThat(grid.focusOnPage(wednesday, page + 1)).isEqualTo(LocalDate(2026, 10, 7))
|
||||||
assertThat(grid.focusOnPage(wednesday, page - 2)).isEqualTo(LocalDate(2026, 9, 16))
|
assertThat(grid.focusOnPage(wednesday, page - 2)).isEqualTo(LocalDate(2026, 9, 16))
|
||||||
@@ -34,7 +34,7 @@ class FocusOnPageTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `a day page is the focus`() {
|
fun `a day page is the focus`() {
|
||||||
val page = DayGrid.pageFor(wednesday)
|
val page = DaysGrid.days.pageFor(wednesday)
|
||||||
assertThat(DayGrid.focusOnPage(wednesday, page + 5)).isEqualTo(LocalDate(2026, 10, 5))
|
assertThat(DaysGrid.days.focusOnPage(wednesday, page + 5)).isEqualTo(LocalDate(2026, 10, 5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class PageNumberingTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `week pages keep their numbering for every first day`() {
|
fun `week pages keep their numbering for every first day`() {
|
||||||
DayOfWeek.entries.forEach { firstDay ->
|
DayOfWeek.entries.forEach { firstDay ->
|
||||||
val grid = WeekGrid(firstDay)
|
val grid = DaysGrid.weeks(firstDay)
|
||||||
assertThat(grid.dayCount).isEqualTo(7)
|
assertThat(grid.dayCount).isEqualTo(7)
|
||||||
assertThat(grid.pageCount).isEqualTo(LEGACY_WEEK_COUNT)
|
assertThat(grid.pageCount).isEqualTo(LEGACY_WEEK_COUNT)
|
||||||
pages(LEGACY_WEEK_COUNT).forEach { page ->
|
pages(LEGACY_WEEK_COUNT).forEach { page ->
|
||||||
@@ -69,7 +69,7 @@ class PageNumberingTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `day pages keep their numbering`() {
|
fun `day pages keep their numbering`() {
|
||||||
val grid = DayGrid
|
val grid = DaysGrid.days
|
||||||
assertThat(grid.dayCount).isEqualTo(1)
|
assertThat(grid.dayCount).isEqualTo(1)
|
||||||
assertThat(grid.pageCount).isEqualTo(LEGACY_DAY_COUNT)
|
assertThat(grid.pageCount).isEqualTo(LEGACY_DAY_COUNT)
|
||||||
pages(LEGACY_DAY_COUNT).forEach { page ->
|
pages(LEGACY_DAY_COUNT).forEach { page ->
|
||||||
@@ -80,15 +80,27 @@ class PageNumberingTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a multi-day jump re-frames exactly when the date would open mid-page`() {
|
||||||
|
val inRange = dates.filter { it.year in 1950..2050 }
|
||||||
|
(3..6).forEach { count ->
|
||||||
|
val grid = DaysGrid.startingAt(LocalDate(2026, 10, 16), count)
|
||||||
|
inRange.forEach { target ->
|
||||||
|
val midPage = grid.startForPage(grid.pageFor(target)) != target
|
||||||
|
assertThat(DaysGrid.startingAt(target, count) != grid).isEqualTo(midPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `grids are equal exactly when they page the same way`() {
|
fun `grids are equal exactly when they page the same way`() {
|
||||||
assertThat(WeekGrid(DayOfWeek.MONDAY)).isEqualTo(WeekGrid(DayOfWeek.MONDAY))
|
assertThat(DaysGrid.weeks(DayOfWeek.MONDAY)).isEqualTo(DaysGrid.weeks(DayOfWeek.MONDAY))
|
||||||
assertThat(WeekGrid(DayOfWeek.MONDAY)).isNotEqualTo(WeekGrid(DayOfWeek.SUNDAY))
|
assertThat(DaysGrid.weeks(DayOfWeek.MONDAY)).isNotEqualTo(DaysGrid.weeks(DayOfWeek.SUNDAY))
|
||||||
assertThat(DaysGrid(3, 1)).isEqualTo(DaysGrid(3, 1))
|
assertThat(DaysGrid(3, 1)).isEqualTo(DaysGrid(3, 1))
|
||||||
assertThat(DaysGrid(3, 1)).isNotEqualTo(DaysGrid(3, 2))
|
assertThat(DaysGrid(3, 1)).isNotEqualTo(DaysGrid(3, 2))
|
||||||
assertThat(DaysGrid(3, 1)).isNotEqualTo(DaysGrid(4, 1))
|
assertThat(DaysGrid(3, 1)).isNotEqualTo(DaysGrid(4, 1))
|
||||||
assertThat(DayGrid).isEqualTo(DayGrid)
|
assertThat(DaysGrid.days).isEqualTo(DaysGrid.days)
|
||||||
assertThat(DayGrid.hashCode()).isEqualTo(DayGrid.hashCode())
|
assertThat(DaysGrid.days.hashCode()).isEqualTo(DaysGrid.days.hashCode())
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
|||||||
@@ -16,34 +16,34 @@ class WeekPagingTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `a page and its week round-trip for every first day`() {
|
fun `a page and its week round-trip for every first day`() {
|
||||||
DayOfWeek.entries.forEach { firstDay ->
|
DayOfWeek.entries.forEach { firstDay ->
|
||||||
val page = weekPageFor(sunday, firstDay)
|
val page = DaysGrid.weeks(firstDay).pageFor(sunday)
|
||||||
val start = weekStartForPage(page, firstDay)
|
val start = DaysGrid.weeks(firstDay).startForPage(page)
|
||||||
assertThat(start).isEqualTo(sunday.startOfWeek(firstDay))
|
assertThat(start).isEqualTo(sunday.startOfWeek(firstDay))
|
||||||
assertThat(weekPageFor(start, firstDay)).isEqualTo(page)
|
assertThat(DaysGrid.weeks(firstDay).pageFor(start)).isEqualTo(page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `every day of a week maps to the same page`() {
|
fun `every day of a week maps to the same page`() {
|
||||||
val start = sunday.startOfWeek(DayOfWeek.MONDAY)
|
val start = sunday.startOfWeek(DayOfWeek.MONDAY)
|
||||||
val pages = (0..6).map { weekPageFor(start.plus(it, DateTimeUnit.DAY), DayOfWeek.MONDAY) }
|
val pages = (0..6).map { DaysGrid.weeks(DayOfWeek.MONDAY).pageFor(start.plus(it, DateTimeUnit.DAY)) }
|
||||||
assertThat(pages.toSet()).hasSize(1)
|
assertThat(pages.toSet()).hasSize(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `adjacent pages are adjacent weeks`() {
|
fun `adjacent pages are adjacent weeks`() {
|
||||||
val page = weekPageFor(sunday, DayOfWeek.SUNDAY)
|
val page = DaysGrid.weeks(DayOfWeek.SUNDAY).pageFor(sunday)
|
||||||
assertThat(weekStartForPage(page + 1, DayOfWeek.SUNDAY))
|
assertThat(DaysGrid.weeks(DayOfWeek.SUNDAY).startForPage(page + 1))
|
||||||
.isEqualTo(LocalDate(2026, 10, 4))
|
.isEqualTo(LocalDate(2026, 10, 4))
|
||||||
assertThat(weekStartForPage(page - 1, DayOfWeek.SUNDAY))
|
assertThat(DaysGrid.weeks(DayOfWeek.SUNDAY).startForPage(page - 1))
|
||||||
.isEqualTo(LocalDate(2026, 9, 20))
|
.isEqualTo(LocalDate(2026, 9, 20))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `dates before the epoch page resolve too`() {
|
fun `dates before the epoch page resolve too`() {
|
||||||
val old = LocalDate(1990, 5, 16)
|
val old = LocalDate(1990, 5, 16)
|
||||||
val page = weekPageFor(old, DayOfWeek.MONDAY)
|
val page = DaysGrid.weeks(DayOfWeek.MONDAY).pageFor(old)
|
||||||
assertThat(weekStartForPage(page, DayOfWeek.MONDAY)).isEqualTo(LocalDate(1990, 5, 14))
|
assertThat(DaysGrid.weeks(DayOfWeek.MONDAY).startForPage(page)).isEqualTo(LocalDate(1990, 5, 14))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -69,14 +69,14 @@ class WeekPagingTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `a day page and its day round-trip`() {
|
fun `a day page and its day round-trip`() {
|
||||||
listOf(LocalDate(2026, 9, 27), LocalDate(1990, 5, 16), LocalDate(2100, 2, 28)).forEach { day ->
|
listOf(LocalDate(2026, 9, 27), LocalDate(1990, 5, 16), LocalDate(2100, 2, 28)).forEach { day ->
|
||||||
assertThat(DayGrid.startForPage(DayGrid.pageFor(day))).isEqualTo(day)
|
assertThat(DaysGrid.days.startForPage(DaysGrid.days.pageFor(day))).isEqualTo(day)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `adjacent day pages are adjacent days across a month end`() {
|
fun `adjacent day pages are adjacent days across a month end`() {
|
||||||
val page = DayGrid.pageFor(LocalDate(2026, 2, 28))
|
val page = DaysGrid.days.pageFor(LocalDate(2026, 2, 28))
|
||||||
assertThat(DayGrid.startForPage(page + 1)).isEqualTo(LocalDate(2026, 3, 1))
|
assertThat(DaysGrid.days.startForPage(page + 1)).isEqualTo(LocalDate(2026, 3, 1))
|
||||||
assertThat(DayGrid.startForPage(page - 1)).isEqualTo(LocalDate(2026, 2, 27))
|
assertThat(DaysGrid.days.startForPage(page - 1)).isEqualTo(LocalDate(2026, 2, 27))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user