Merge pull request 'feat: current-time line in day and week views' (!31) from feat/now-line into release/v2.8.0
Reviewed-on: #31
This commit was merged in pull request #31.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package de.jeanlucmakiola.calendula.ui.common
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
private val DotSize = 8.dp
|
||||
private val LineThickness = 2.dp
|
||||
|
||||
/**
|
||||
* Current wall-clock instant that updates once a minute, re-aligning to each
|
||||
* minute boundary (rather than ticking a fixed 60 s) so it never drifts toward
|
||||
* the middle of a minute. Drives the "now" line in the day/week grids.
|
||||
*/
|
||||
@Composable
|
||||
private fun rememberCurrentMinute(): State<Instant> {
|
||||
val instant = remember { mutableStateOf(Clock.System.now()) }
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
val now = Clock.System.now()
|
||||
instant.value = now
|
||||
delay(60_000L - now.toEpochMilliseconds() % 60_000L)
|
||||
}
|
||||
}
|
||||
return instant
|
||||
}
|
||||
|
||||
/**
|
||||
* A thin "current time" indicator — a leading dot plus a line — drawn across a
|
||||
* day column. Positioned on the same [hourHeight] scale the event blocks use so
|
||||
* it lines up with the grid, and refreshed each minute. Renders nothing unless
|
||||
* the wall clock is on [date]; callers mount it only for the column showing
|
||||
* today, so the per-minute tick runs on a single column.
|
||||
*/
|
||||
@Composable
|
||||
fun NowLine(
|
||||
date: LocalDate,
|
||||
hourHeight: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val now by rememberCurrentMinute()
|
||||
val local = now.toLocalDateTime(TimeZone.currentSystemDefault())
|
||||
if (local.date != date) return
|
||||
|
||||
val minutes = local.hour * 60 + local.minute
|
||||
val top = hourHeight * (minutes / 60f)
|
||||
val color = MaterialTheme.colorScheme.primary
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(DotSize)
|
||||
.offset(y = top - DotSize / 2),
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(LineThickness)
|
||||
.background(color),
|
||||
)
|
||||
// The dot anchors the line to the gutter edge, mirroring the standard
|
||||
// calendar "now" marker; drawn after the line so it sits on top.
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(DotSize)
|
||||
.background(color, CircleShape),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,7 @@ import de.jeanlucmakiola.calendula.ui.common.CalendarDrawer
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarFabColumn
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarFailure
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
||||
import de.jeanlucmakiola.calendula.ui.common.NowLine
|
||||
import de.jeanlucmakiola.calendula.ui.common.ViewSwitcherPill
|
||||
import de.jeanlucmakiola.calendula.ui.common.calendarSlideTransition
|
||||
import de.jeanlucmakiola.calendula.ui.common.next
|
||||
@@ -496,6 +497,7 @@ private fun Timeline(
|
||||
blocks = state.timed,
|
||||
dark = dark,
|
||||
date = state.date,
|
||||
today = state.today,
|
||||
onEventClick = onEventClick,
|
||||
onCreateAt = onCreateAt,
|
||||
modifier = Modifier
|
||||
@@ -512,6 +514,7 @@ private fun DayColumnCard(
|
||||
blocks: List<TimedBlock>,
|
||||
dark: Boolean,
|
||||
date: LocalDate,
|
||||
today: LocalDate,
|
||||
onEventClick: (EventInstance) -> Unit,
|
||||
onCreateAt: (LocalDate, Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -556,6 +559,10 @@ private fun DayColumnCard(
|
||||
.padding(horizontal = 1.dp),
|
||||
)
|
||||
}
|
||||
// Current-time line, on top of the events, only on today's column.
|
||||
if (date == today) {
|
||||
NowLine(date = date, hourHeight = HOUR_HEIGHT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ import de.jeanlucmakiola.calendula.ui.common.CalendarDrawer
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarFabColumn
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarFailure
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
||||
import de.jeanlucmakiola.calendula.ui.common.NowLine
|
||||
import de.jeanlucmakiola.calendula.ui.common.ViewSwitcherPill
|
||||
import de.jeanlucmakiola.calendula.ui.common.calendarSlideTransition
|
||||
import de.jeanlucmakiola.calendula.ui.common.currentLocale
|
||||
@@ -610,6 +611,7 @@ private fun Timeline(
|
||||
blocks = state.timedByDay[day].orEmpty(),
|
||||
dark = dark,
|
||||
date = day,
|
||||
today = state.today,
|
||||
onEventClick = onEventClick,
|
||||
onCreateAt = onCreateAt,
|
||||
modifier = Modifier
|
||||
@@ -628,6 +630,7 @@ private fun DayColumnCard(
|
||||
blocks: List<TimedBlock>,
|
||||
dark: Boolean,
|
||||
date: LocalDate,
|
||||
today: LocalDate,
|
||||
onEventClick: (EventInstance) -> Unit,
|
||||
onCreateAt: (LocalDate, Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -671,6 +674,10 @@ private fun DayColumnCard(
|
||||
.padding(horizontal = 1.dp),
|
||||
)
|
||||
}
|
||||
// Current-time line, on top of the events, only on today's column.
|
||||
if (date == today) {
|
||||
NowLine(date = date, hourHeight = HOUR_HEIGHT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user