feat(nav): jump-to-date action in the navigation drawer #3
@@ -169,7 +169,9 @@ unblock a later item. Order is a plan, not a contract — revisit after each lan
|
|||||||
4. ~~Per-event color~~ *(shipped v2.4.0)* — palette calendars write
|
4. ~~Per-event color~~ *(shipped v2.4.0)* — palette calendars write
|
||||||
`EVENT_COLOR_KEY` (sync-safe); local/opted-in calendars write a raw
|
`EVENT_COLOR_KEY` (sync-safe); local/opted-in calendars write a raw
|
||||||
`EVENT_COLOR`; off-by-default setting for no-palette synced calendars
|
`EVENT_COLOR`; off-by-default setting for no-palette synced calendars
|
||||||
5. **Duplicate event** *(next)* — detail action → prefilled create form; near-free on the tap-to-create prefill infra
|
Tier 1's create/edit + calendars arc is effectively closed. **Duplicate event**
|
||||||
|
was deprioritised (2026-06-17) as low-importance and dropped to the bottom of
|
||||||
|
the sequence; the next item is now **Jump-to-date** (formerly Tier 2).
|
||||||
|
|
||||||
(Tier 2+ numbering below shifts accordingly; ranking unchanged.)
|
(Tier 2+ numbering below shifts accordingly; ranking unchanged.)
|
||||||
|
|
||||||
@@ -223,7 +225,7 @@ Out of scope (no new settings *features* here) — this is a structure + style
|
|||||||
pass on the existing controls; new toggles ride in with their own features.
|
pass on the existing controls; new toggles ride in with their own features.
|
||||||
|
|
||||||
**Tier 2 — navigation & daily-driver completeness**
|
**Tier 2 — navigation & daily-driver completeness**
|
||||||
5. Jump-to-date — drawer date picker (un-cut from V1); cheap, fills the nav gap
|
5. Jump-to-date — drawer date picker (un-cut from V1); cheap, fills the nav gap *(next)*
|
||||||
6. Agenda view — the missing 4th view; serves daily-driver users *and* becomes the data source for the widget
|
6. Agenda view — the missing 4th view; serves daily-driver users *and* becomes the data source for the widget
|
||||||
|
|
||||||
**Tier 3 — platform reach (depends on Tier 2)**
|
**Tier 3 — platform reach (depends on Tier 2)**
|
||||||
@@ -240,6 +242,10 @@ pass on the existing controls; new toggles ride in with their own features.
|
|||||||
- Locations & People — contact address picker (no-permission, one-shot) is the safe entry; OSM autocomplete needs INTERNET
|
- Locations & People — contact address picker (no-permission, one-shot) is the safe entry; OSM autocomplete needs INTERNET
|
||||||
- Move event to another calendar — sync-adapter minefield (copy+delete model)
|
- Move event to another calendar — sync-adapter minefield (copy+delete model)
|
||||||
|
|
||||||
|
**Bottom — deprioritised, not important**
|
||||||
|
- Duplicate event (detail action → prefilled create form) — moved here
|
||||||
|
2026-06-17; cheap but low value, pick up only if asked
|
||||||
|
|
||||||
**Unranked / fill-in** — pinch-to-zoom time scale, tablet/foldable layouts,
|
**Unranked / fill-in** — pinch-to-zoom time scale, tablet/foldable layouts,
|
||||||
full-text search, ICS file import. Pulled in opportunistically, not sequenced.
|
full-text search, ICS file import. Pulled in opportunistically, not sequenced.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package de.jeanlucmakiola.calendula.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.material3.DatePicker
|
||||||
|
import androidx.compose.material3.DatePickerDialog
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.rememberDatePickerState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import de.jeanlucmakiola.calendula.R
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
|
||||||
|
/** One UTC day in milliseconds — the unit the M3 [DatePicker] speaks. */
|
||||||
|
const val MILLIS_PER_DAY: Long = 86_400_000L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The app's standard Material 3 date picker, opened on [initial] and reporting
|
||||||
|
* the chosen day through [onConfirm]. Shared by the event form (start/end date,
|
||||||
|
* RRULE until) and the drawer's jump-to-date action.
|
||||||
|
*
|
||||||
|
* DatePicker speaks UTC-midnight millis; epoch-day arithmetic keeps the
|
||||||
|
* conversion zone-proof in both directions.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun CalendarDatePickerDialog(
|
||||||
|
initial: LocalDate,
|
||||||
|
onConfirm: (LocalDate) -> Unit,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
) {
|
||||||
|
val state = rememberDatePickerState(
|
||||||
|
initialSelectedDateMillis = initial.toEpochDays() * MILLIS_PER_DAY,
|
||||||
|
)
|
||||||
|
DatePickerDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
state.selectedDateMillis?.let { millis ->
|
||||||
|
onConfirm(LocalDate.fromEpochDays((millis / MILLIS_PER_DAY).toInt()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) { Text(stringResource(R.string.dialog_ok)) }
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) { Text(stringResource(R.string.dialog_cancel)) }
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
DatePicker(state = state)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,12 +17,17 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.DateRange
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalDrawerSheet
|
import androidx.compose.material3.ModalDrawerSheet
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -32,23 +37,31 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.jeanlucmakiola.calendula.R
|
import de.jeanlucmakiola.calendula.R
|
||||||
import de.jeanlucmakiola.calendula.ui.filter.CalendarFilterList
|
import de.jeanlucmakiola.calendula.ui.filter.CalendarFilterList
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigation drawer shared by every top-level calendar screen.
|
* Navigation drawer shared by every top-level calendar screen.
|
||||||
*
|
*
|
||||||
* Uses the app's grouped-card design system (see [GroupedRow]): a branded
|
* Uses the app's grouped-card design system (see [GroupedRow]): a branded
|
||||||
* header, the View switcher as a grouped card (the active view highlighted),
|
* header, the View switcher as a grouped card (the active view highlighted),
|
||||||
* the per-calendar visibility filter (M3) inline, and a pinned Settings row.
|
* a jump-to-date action, the per-calendar visibility filter (M3) inline, and a
|
||||||
* The "View" section mirrors the top-bar switcher pill — tapping a view here
|
* pinned Settings row. The "View" section mirrors the top-bar switcher pill —
|
||||||
* selects it (and closes the drawer) rather than cycling. The host screen owns
|
* tapping a view here selects it (and closes the drawer) rather than cycling.
|
||||||
* the drawer state.
|
* The host screen owns the drawer state.
|
||||||
|
*
|
||||||
|
* [currentDate] seeds the jump-to-date picker (the visible day/week-start/month
|
||||||
|
* anchor); [onJumpToDate] navigates the active view to the chosen day.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun CalendarDrawer(
|
fun CalendarDrawer(
|
||||||
currentView: CalendarView,
|
currentView: CalendarView,
|
||||||
|
currentDate: LocalDate,
|
||||||
onSelectView: (CalendarView) -> Unit,
|
onSelectView: (CalendarView) -> Unit,
|
||||||
|
onJumpToDate: (LocalDate) -> Unit,
|
||||||
onSettings: () -> Unit,
|
onSettings: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
var showDatePicker by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
ModalDrawerSheet {
|
ModalDrawerSheet {
|
||||||
// The whole sidebar scrolls as one — header, views, the calendar filter
|
// The whole sidebar scrolls as one — header, views, the calendar filter
|
||||||
// and Settings all flow in a single scroll container.
|
// and Settings all flow in a single scroll container.
|
||||||
@@ -71,6 +84,15 @@ fun CalendarDrawer(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
GroupedRow(
|
||||||
|
title = stringResource(R.string.drawer_jump_to_date),
|
||||||
|
position = Position.Alone,
|
||||||
|
minHeight = 56.dp,
|
||||||
|
leading = { Icon(Icons.Filled.DateRange, contentDescription = null) },
|
||||||
|
onClick = { showDatePicker = true },
|
||||||
|
)
|
||||||
|
|
||||||
Spacer(Modifier.height(16.dp))
|
Spacer(Modifier.height(16.dp))
|
||||||
|
|
||||||
DrawerSectionHeader(stringResource(R.string.filter_title))
|
DrawerSectionHeader(stringResource(R.string.filter_title))
|
||||||
@@ -87,6 +109,17 @@ fun CalendarDrawer(
|
|||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showDatePicker) {
|
||||||
|
CalendarDatePickerDialog(
|
||||||
|
initial = currentDate,
|
||||||
|
onConfirm = {
|
||||||
|
showDatePicker = false
|
||||||
|
onJumpToDate(it)
|
||||||
|
},
|
||||||
|
onDismiss = { showDatePicker = false },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Branded header: the app-icon chip beside the app name. */
|
/** Branded header: the app-icon chip beside the app name. */
|
||||||
|
|||||||
@@ -151,6 +151,11 @@ fun DayScreen(
|
|||||||
}
|
}
|
||||||
viewModel.goToToday()
|
viewModel.goToToday()
|
||||||
}
|
}
|
||||||
|
// Drawer jump-to-date: slide from the side the target lies on.
|
||||||
|
val jumpToDate: (LocalDate) -> Unit = { target ->
|
||||||
|
slideDir = if (target < date) -1 else 1
|
||||||
|
viewModel.goToDate(target)
|
||||||
|
}
|
||||||
|
|
||||||
ModalNavigationDrawer(
|
ModalNavigationDrawer(
|
||||||
drawerState = drawerState,
|
drawerState = drawerState,
|
||||||
@@ -159,10 +164,15 @@ fun DayScreen(
|
|||||||
drawerContent = {
|
drawerContent = {
|
||||||
CalendarDrawer(
|
CalendarDrawer(
|
||||||
currentView = selectedView,
|
currentView = selectedView,
|
||||||
|
currentDate = date,
|
||||||
onSelectView = { view ->
|
onSelectView = { view ->
|
||||||
onSelectView(view)
|
onSelectView(view)
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
},
|
},
|
||||||
|
onJumpToDate = { target ->
|
||||||
|
jumpToDate(target)
|
||||||
|
scope.launch { drawerState.close() }
|
||||||
|
},
|
||||||
onSettings = {
|
onSettings = {
|
||||||
onOpenSettings()
|
onOpenSettings()
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ import androidx.compose.material.icons.filled.VisibilityOff
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.AssistChip
|
import androidx.compose.material3.AssistChip
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.DatePicker
|
|
||||||
import androidx.compose.material3.DatePickerDialog
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -73,7 +71,6 @@ import androidx.compose.material3.TextButton
|
|||||||
import androidx.compose.material3.TimePicker
|
import androidx.compose.material3.TimePicker
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberDatePickerState
|
|
||||||
import androidx.compose.material3.rememberTimePickerState
|
import androidx.compose.material3.rememberTimePickerState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -113,7 +110,9 @@ import de.jeanlucmakiola.calendula.domain.SimpleRecurrence
|
|||||||
import de.jeanlucmakiola.calendula.domain.parseSimpleRecurrence
|
import de.jeanlucmakiola.calendula.domain.parseSimpleRecurrence
|
||||||
import de.jeanlucmakiola.calendula.domain.toRRule
|
import de.jeanlucmakiola.calendula.domain.toRRule
|
||||||
import de.jeanlucmakiola.calendula.ui.common.CALENDAR_COLOR_PALETTE
|
import de.jeanlucmakiola.calendula.ui.common.CALENDAR_COLOR_PALETTE
|
||||||
|
import de.jeanlucmakiola.calendula.ui.common.CalendarDatePickerDialog
|
||||||
import de.jeanlucmakiola.calendula.ui.common.ColorSwatchRow
|
import de.jeanlucmakiola.calendula.ui.common.ColorSwatchRow
|
||||||
|
import de.jeanlucmakiola.calendula.ui.common.MILLIS_PER_DAY
|
||||||
import de.jeanlucmakiola.calendula.ui.common.InlineTextField
|
import de.jeanlucmakiola.calendula.ui.common.InlineTextField
|
||||||
import de.jeanlucmakiola.calendula.ui.common.OptionCard
|
import de.jeanlucmakiola.calendula.ui.common.OptionCard
|
||||||
import de.jeanlucmakiola.calendula.ui.common.currentLocale
|
import de.jeanlucmakiola.calendula.ui.common.currentLocale
|
||||||
@@ -786,12 +785,12 @@ private fun EventEditContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
when (picker) {
|
when (picker) {
|
||||||
PickerTarget.StartDate -> DatePickerAlert(
|
PickerTarget.StartDate -> CalendarDatePickerDialog(
|
||||||
initial = form.start.date,
|
initial = form.start.date,
|
||||||
onConfirm = { viewModel.setStartDate(it); picker = null },
|
onConfirm = { viewModel.setStartDate(it); picker = null },
|
||||||
onDismiss = { picker = null },
|
onDismiss = { picker = null },
|
||||||
)
|
)
|
||||||
PickerTarget.EndDate -> DatePickerAlert(
|
PickerTarget.EndDate -> CalendarDatePickerDialog(
|
||||||
initial = form.end.date,
|
initial = form.end.date,
|
||||||
onConfirm = { viewModel.setEndDate(it); picker = null },
|
onConfirm = { viewModel.setEndDate(it); picker = null },
|
||||||
onDismiss = { picker = null },
|
onDismiss = { picker = null },
|
||||||
@@ -1178,7 +1177,7 @@ private fun RecurrencePickerDialog(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (showUntilPicker) {
|
if (showUntilPicker) {
|
||||||
DatePickerAlert(
|
CalendarDatePickerDialog(
|
||||||
initial = untilDate ?: LocalDate.fromEpochDays(
|
initial = untilDate ?: LocalDate.fromEpochDays(
|
||||||
(Clock.System.now().toEpochMilliseconds() / MILLIS_PER_DAY).toInt(),
|
(Clock.System.now().toEpochMilliseconds() / MILLIS_PER_DAY).toInt(),
|
||||||
),
|
),
|
||||||
@@ -1688,37 +1687,6 @@ private fun ScheduleRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun DatePickerAlert(
|
|
||||||
initial: LocalDate,
|
|
||||||
onConfirm: (LocalDate) -> Unit,
|
|
||||||
onDismiss: () -> Unit,
|
|
||||||
) {
|
|
||||||
// DatePicker speaks UTC-midnight millis; epoch-day arithmetic keeps the
|
|
||||||
// conversion zone-proof in both directions.
|
|
||||||
val state = rememberDatePickerState(
|
|
||||||
initialSelectedDateMillis = initial.toEpochDays() * MILLIS_PER_DAY,
|
|
||||||
)
|
|
||||||
DatePickerDialog(
|
|
||||||
onDismissRequest = onDismiss,
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
state.selectedDateMillis?.let { millis ->
|
|
||||||
onConfirm(LocalDate.fromEpochDays((millis / MILLIS_PER_DAY).toInt()))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) { Text(stringResource(R.string.dialog_ok)) }
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.dialog_cancel)) }
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
DatePicker(state = state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TimePickerAlert(
|
private fun TimePickerAlert(
|
||||||
@@ -1775,5 +1743,3 @@ private fun CalendarPickerDialog(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val MILLIS_PER_DAY = 86_400_000L
|
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ fun MonthScreen(
|
|||||||
}
|
}
|
||||||
viewModel.goToToday()
|
viewModel.goToToday()
|
||||||
}
|
}
|
||||||
|
// Drawer jump-to-date: slide from the side the target month lies on.
|
||||||
|
val jumpToDate: (LocalDate) -> Unit = { target ->
|
||||||
|
slideDir = if (YearMonth(target.year, target.month) < month) -1 else 1
|
||||||
|
viewModel.goToDate(target)
|
||||||
|
}
|
||||||
|
|
||||||
ModalNavigationDrawer(
|
ModalNavigationDrawer(
|
||||||
drawerState = drawerState,
|
drawerState = drawerState,
|
||||||
@@ -132,10 +137,15 @@ fun MonthScreen(
|
|||||||
drawerContent = {
|
drawerContent = {
|
||||||
CalendarDrawer(
|
CalendarDrawer(
|
||||||
currentView = selectedView,
|
currentView = selectedView,
|
||||||
|
currentDate = LocalDate(month.year, month.month, 1),
|
||||||
onSelectView = { view ->
|
onSelectView = { view ->
|
||||||
onSelectView(view)
|
onSelectView(view)
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
},
|
},
|
||||||
|
onJumpToDate = { target ->
|
||||||
|
jumpToDate(target)
|
||||||
|
scope.launch { drawerState.close() }
|
||||||
|
},
|
||||||
onSettings = {
|
onSettings = {
|
||||||
onOpenSettings()
|
onOpenSettings()
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ class MonthViewModel @Inject constructor(
|
|||||||
_month.value = YearMonth(todayDate.year, todayDate.month)
|
_month.value = YearMonth(todayDate.year, todayDate.month)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Jump to the month containing [date] (drawer jump-to-date). */
|
||||||
|
fun goToDate(date: LocalDate) {
|
||||||
|
_month.value = YearMonth(date.year, date.month)
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildState(
|
private fun buildState(
|
||||||
ym: YearMonth,
|
ym: YearMonth,
|
||||||
weekStart: DayOfWeek,
|
weekStart: DayOfWeek,
|
||||||
|
|||||||
@@ -156,6 +156,11 @@ fun WeekScreen(
|
|||||||
}
|
}
|
||||||
viewModel.goToToday()
|
viewModel.goToToday()
|
||||||
}
|
}
|
||||||
|
// Drawer jump-to-date: slide from the side the target week lies on.
|
||||||
|
val jumpToDate: (LocalDate) -> Unit = { target ->
|
||||||
|
slideDir = if (target < weekStart) -1 else 1
|
||||||
|
viewModel.goToDate(target)
|
||||||
|
}
|
||||||
|
|
||||||
ModalNavigationDrawer(
|
ModalNavigationDrawer(
|
||||||
drawerState = drawerState,
|
drawerState = drawerState,
|
||||||
@@ -164,10 +169,15 @@ fun WeekScreen(
|
|||||||
drawerContent = {
|
drawerContent = {
|
||||||
CalendarDrawer(
|
CalendarDrawer(
|
||||||
currentView = selectedView,
|
currentView = selectedView,
|
||||||
|
currentDate = weekStart,
|
||||||
onSelectView = { view ->
|
onSelectView = { view ->
|
||||||
onSelectView(view)
|
onSelectView(view)
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
},
|
},
|
||||||
|
onJumpToDate = { target ->
|
||||||
|
jumpToDate(target)
|
||||||
|
scope.launch { drawerState.close() }
|
||||||
|
},
|
||||||
onSettings = {
|
onSettings = {
|
||||||
onOpenSettings()
|
onOpenSettings()
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
|
|||||||
@@ -107,6 +107,11 @@ class WeekViewModel @Inject constructor(
|
|||||||
_anchor.value = todayDate
|
_anchor.value = todayDate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Jump to the week containing [date] (drawer jump-to-date). */
|
||||||
|
fun goToDate(date: LocalDate) {
|
||||||
|
_anchor.value = date
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildState(
|
private fun buildState(
|
||||||
start: LocalDate,
|
start: LocalDate,
|
||||||
calendars: List<CalendarSource>,
|
calendars: List<CalendarSource>,
|
||||||
|
|||||||
@@ -198,6 +198,9 @@
|
|||||||
<string name="view_day">Tag</string>
|
<string name="view_day">Tag</string>
|
||||||
<string name="view_section">Ansicht</string>
|
<string name="view_section">Ansicht</string>
|
||||||
|
|
||||||
|
<!-- Zu Datum springen (Navigationsleiste) -->
|
||||||
|
<string name="drawer_jump_to_date">Zu Datum springen</string>
|
||||||
|
|
||||||
<!-- Kalender-Filter (M3) -->
|
<!-- Kalender-Filter (M3) -->
|
||||||
<string name="filter_title">Kalender</string>
|
<string name="filter_title">Kalender</string>
|
||||||
|
|
||||||
|
|||||||
@@ -199,6 +199,9 @@
|
|||||||
<string name="view_day">Day</string>
|
<string name="view_day">Day</string>
|
||||||
<string name="view_section">View</string>
|
<string name="view_section">View</string>
|
||||||
|
|
||||||
|
<!-- Jump to date (drawer) -->
|
||||||
|
<string name="drawer_jump_to_date">Jump to date</string>
|
||||||
|
|
||||||
<!-- Calendar filter (M3) -->
|
<!-- Calendar filter (M3) -->
|
||||||
<string name="filter_title">Calendars</string>
|
<string name="filter_title">Calendars</string>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user