Share the unnamed-calendar label, visibility switch and manager row
This commit is contained in:
@@ -53,6 +53,7 @@ import de.jeanlucmakiola.calendula.ui.common.CalendarColorChip
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarFailure
|
||||
import de.jeanlucmakiola.calendula.ui.common.LeadingAvatar
|
||||
import de.jeanlucmakiola.calendula.ui.common.LocalManageCalendars
|
||||
import de.jeanlucmakiola.calendula.ui.common.orUnnamed
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.DialogAmountField
|
||||
import de.jeanlucmakiola.floret.components.DialogUnitDropdown
|
||||
@@ -317,7 +318,7 @@ private fun ExportCalendarPicker(
|
||||
calendars.forEachIndexed { index, calendar ->
|
||||
val isSelected = calendar.id in selected
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
title = calendar.displayName.orUnnamed(),
|
||||
summary = calendar.description,
|
||||
position = positionOf(index, calendars.size),
|
||||
leading = { CalendarColorChip(calendar.color) },
|
||||
|
||||
@@ -40,7 +40,6 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -73,6 +72,7 @@ import de.jeanlucmakiola.calendula.domain.stateLabels
|
||||
import de.jeanlucmakiola.calendula.data.calendar.CalendarColorPalette
|
||||
import de.jeanlucmakiola.calendula.ui.common.AccountKey
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarColorChip
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarVisibilitySwitch
|
||||
import de.jeanlucmakiola.calendula.ui.common.accountGroupTitle
|
||||
import de.jeanlucmakiola.calendula.ui.common.groupByAccount
|
||||
import de.jeanlucmakiola.calendula.ui.common.LocalSoftenColors
|
||||
@@ -82,6 +82,7 @@ import de.jeanlucmakiola.calendula.ui.common.eventInk
|
||||
import de.jeanlucmakiola.calendula.ui.common.LeadingAvatar
|
||||
import de.jeanlucmakiola.calendula.ui.common.SourceLogo
|
||||
import de.jeanlucmakiola.calendula.ui.common.curatedSourcePackage
|
||||
import de.jeanlucmakiola.calendula.ui.common.orUnnamed
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedSectionHeader
|
||||
@@ -238,21 +239,10 @@ private fun CalendarsList(
|
||||
GroupedHint(stringResource(R.string.calendars_local_empty), style = MaterialTheme.typography.bodySmall)
|
||||
} else {
|
||||
local.forEachIndexed { index, calendar ->
|
||||
val disabled = !calendar.isVisibleInSystem
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
summary = calendarRowSummary(calendar),
|
||||
position = if (index == local.lastIndex) Position.Bottom else Position.Middle,
|
||||
container = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
dimmed = disabled,
|
||||
leading = { CalendarColorChip(calendar.color, dimIf(disabled)) },
|
||||
trailing = {
|
||||
EnableSwitch(
|
||||
calendarName = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
enabled = !disabled,
|
||||
onToggle = { enabled -> onSetVisible(calendar.id, enabled) },
|
||||
)
|
||||
},
|
||||
CalendarManagerRow(
|
||||
calendar = calendar,
|
||||
last = index == local.lastIndex,
|
||||
onSetVisible = onSetVisible,
|
||||
onClick = { onEdit(calendar) },
|
||||
)
|
||||
}
|
||||
@@ -323,27 +313,10 @@ private fun CalendarsList(
|
||||
// bottom, dimmed and switchless.
|
||||
val ordered = cals.orderedForManager()
|
||||
ordered.forEachIndexed { index, calendar ->
|
||||
val disabled = !calendar.isVisibleInSystem || calendar.isNotSynced
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
summary = calendarRowSummary(calendar),
|
||||
position = if (index == ordered.lastIndex) Position.Bottom else Position.Middle,
|
||||
container = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
dimmed = disabled,
|
||||
leading = { CalendarColorChip(calendar.color, dimIf(disabled)) },
|
||||
trailing = if (calendar.hasVisibilitySwitch) {
|
||||
{
|
||||
EnableSwitch(
|
||||
calendarName = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
enabled = calendar.isVisibleInSystem,
|
||||
onToggle = { enabled ->
|
||||
onSetVisible(calendar.id, enabled)
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
CalendarManagerRow(
|
||||
calendar = calendar,
|
||||
last = index == ordered.lastIndex,
|
||||
onSetVisible = onSetVisible,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -682,17 +655,37 @@ private fun calendarRowSummary(calendar: CalendarSource): String? {
|
||||
* `Calendars.VISIBLE`. Unchecked drops the calendar out of every surface and
|
||||
* stops its reminders. Carries its own content description.
|
||||
*/
|
||||
/**
|
||||
* One calendar in a group: dimmed while switched off or not syncing, with the
|
||||
* visibility switch only where it can change anything.
|
||||
*/
|
||||
@Composable
|
||||
private fun EnableSwitch(
|
||||
calendarName: String,
|
||||
enabled: Boolean,
|
||||
onToggle: (Boolean) -> Unit,
|
||||
private fun CalendarManagerRow(
|
||||
calendar: CalendarSource,
|
||||
last: Boolean,
|
||||
onSetVisible: (Long, Boolean) -> Unit,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
val label = stringResource(R.string.calendars_visibility_a11y, calendarName)
|
||||
Switch(
|
||||
checked = enabled,
|
||||
onCheckedChange = onToggle,
|
||||
modifier = Modifier.semantics { contentDescription = label },
|
||||
val disabled = !calendar.isVisibleInSystem || calendar.isNotSynced
|
||||
GroupedRow(
|
||||
title = calendar.displayName.orUnnamed(),
|
||||
summary = calendarRowSummary(calendar),
|
||||
position = if (last) Position.Bottom else Position.Middle,
|
||||
container = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
dimmed = disabled,
|
||||
leading = { CalendarColorChip(calendar.color, dimIf(disabled)) },
|
||||
trailing = if (calendar.hasVisibilitySwitch) {
|
||||
{
|
||||
CalendarVisibilitySwitch(
|
||||
calendarName = calendar.displayName.orUnnamed(),
|
||||
enabled = calendar.isVisibleInSystem,
|
||||
onToggle = { enabled -> onSetVisible(calendar.id, enabled) },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ fun List<CalendarSource>.groupByAccount(): List<CalendarAccountGroup> {
|
||||
*/
|
||||
@Composable
|
||||
fun accountGroupTitle(group: CalendarAccountGroup): String {
|
||||
val label = group.label.ifBlank { stringResource(R.string.calendar_unnamed) }
|
||||
val label = group.label.orUnnamed()
|
||||
return if (!group.ambiguous) {
|
||||
label
|
||||
} else {
|
||||
|
||||
@@ -114,7 +114,7 @@ private fun CalendarPickerGroup(
|
||||
)
|
||||
calendars.forEachIndexed { index, calendar ->
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
title = calendar.displayName.orUnnamed(),
|
||||
position = if (index == calendars.lastIndex) Position.Bottom else Position.Middle,
|
||||
checked = calendar.id == selectedId,
|
||||
leading = { CalendarColorChip(calendar.color) },
|
||||
|
||||
@@ -3,10 +3,12 @@ package de.jeanlucmakiola.calendula.ui.common
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.style.LineHeightStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import de.jeanlucmakiola.calendula.R
|
||||
|
||||
/**
|
||||
* The calendar surfaces' line-height treatment. Material wraps an 11sp glyph in
|
||||
@@ -55,3 +57,7 @@ fun rememberTrimmedLineHeight(style: TextStyle): Dp {
|
||||
with(density) { measurer.measure(LINE_SAMPLE, style.trimmedLines()).size.height.toDp() }
|
||||
}
|
||||
}
|
||||
|
||||
/** A calendar or account name, or the "unnamed" label when it is blank. */
|
||||
@Composable
|
||||
fun String.orUnnamed(): String = ifBlank { stringResource(R.string.calendar_unnamed) }
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.jeanlucmakiola.calendula.ui.common
|
||||
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import de.jeanlucmakiola.calendula.R
|
||||
|
||||
/** The per-row switch writing the device's own `Calendars.VISIBLE`, as Settings does (#75). */
|
||||
@Composable
|
||||
fun CalendarVisibilitySwitch(
|
||||
calendarName: String,
|
||||
enabled: Boolean,
|
||||
onToggle: (Boolean) -> Unit,
|
||||
) {
|
||||
val label = stringResource(R.string.calendars_visibility_a11y, calendarName)
|
||||
Switch(
|
||||
checked = enabled,
|
||||
onCheckedChange = onToggle,
|
||||
modifier = Modifier.semantics { contentDescription = label },
|
||||
)
|
||||
}
|
||||
@@ -90,6 +90,7 @@ import de.jeanlucmakiola.calendula.domain.TimeZoneOption
|
||||
import de.jeanlucmakiola.calendula.domain.timeZoneOptionOf
|
||||
import de.jeanlucmakiola.calendula.domain.zoneDescriptor
|
||||
import de.jeanlucmakiola.calendula.domain.zoneOrNull
|
||||
import de.jeanlucmakiola.calendula.ui.common.orUnnamed
|
||||
import de.jeanlucmakiola.floret.components.IconInfoCard
|
||||
import de.jeanlucmakiola.floret.identity.LocalFloretDarkTheme
|
||||
import de.jeanlucmakiola.floret.identity.predictiveBack
|
||||
@@ -446,7 +447,7 @@ private fun EventDetailContent(
|
||||
iconContentDescription = stringResource(R.string.event_detail_calendar),
|
||||
) {
|
||||
Text(
|
||||
text = state.calendarName?.ifBlank { stringResource(R.string.calendar_unnamed) }
|
||||
text = state.calendarName?.orUnnamed()
|
||||
?: stringResource(R.string.event_detail_calendar_unknown),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
|
||||
@@ -33,6 +33,9 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Notes
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import de.jeanlucmakiola.calendula.ui.common.orUnnamed
|
||||
import de.jeanlucmakiola.floret.components.IconInfoCard
|
||||
import de.jeanlucmakiola.floret.identity.LocalFloretDarkTheme
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -85,11 +88,9 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
@@ -832,7 +833,7 @@ private fun EventEditContent(
|
||||
text = stringResource(
|
||||
R.string.event_edit_managed_hint,
|
||||
selectedCalendar?.displayName
|
||||
?.ifBlank { stringResource(R.string.calendar_unnamed) }
|
||||
?.orUnnamed()
|
||||
.orEmpty(),
|
||||
),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
@@ -939,7 +940,7 @@ private fun EventEditContent(
|
||||
) {
|
||||
Text(
|
||||
text = selectedCalendar?.displayName
|
||||
?.ifBlank { stringResource(R.string.calendar_unnamed) }
|
||||
?.orUnnamed()
|
||||
?: stringResource(R.string.event_edit_error_no_calendar),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = if (selectedCalendar == null) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.calendula.domain.FailureReason
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarColorChip
|
||||
import de.jeanlucmakiola.calendula.ui.common.orUnnamed
|
||||
import de.jeanlucmakiola.calendula.ui.common.sourceAppName
|
||||
import de.jeanlucmakiola.floret.components.GroupedListInset
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
@@ -62,7 +63,7 @@ private fun FilterList(
|
||||
) {
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
groups.forEach { group ->
|
||||
val account = group.account.ifBlank { stringResource(R.string.calendar_unnamed) }
|
||||
val account = group.account.orUnnamed()
|
||||
Text(
|
||||
text = if (group.ambiguous) {
|
||||
stringResource(
|
||||
@@ -84,7 +85,7 @@ private fun FilterList(
|
||||
)
|
||||
group.calendars.forEachIndexed { index, cal ->
|
||||
GroupedRow(
|
||||
title = cal.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
title = cal.displayName.orUnnamed(),
|
||||
position = positionOf(index, group.calendars.size),
|
||||
minHeight = 56.dp,
|
||||
leading = { CalendarColorChip(cal.color) },
|
||||
|
||||
@@ -8,20 +8,19 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.calendula.domain.CalendarSource
|
||||
import de.jeanlucmakiola.calendula.domain.hasVisibilitySwitch
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarColorChip
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarVisibilitySwitch
|
||||
import de.jeanlucmakiola.calendula.ui.common.orUnnamed
|
||||
import de.jeanlucmakiola.floret.components.GroupedListInset
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.OnboardingPrimaryButton
|
||||
@@ -122,7 +121,7 @@ internal fun HiddenCalendarsStep(
|
||||
calendars.forEachIndexed { index, calendar ->
|
||||
val off = !calendar.isVisibleInSystem
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
title = calendar.displayName.orUnnamed(),
|
||||
summary = calendar.accountName.takeIf { it.isNotBlank() },
|
||||
position = when {
|
||||
calendars.size == 1 -> Position.Alone
|
||||
@@ -133,8 +132,8 @@ internal fun HiddenCalendarsStep(
|
||||
dimmed = off,
|
||||
trailing = if (calendar.hasVisibilitySwitch) {
|
||||
{
|
||||
CalendarSwitch(
|
||||
calendarName = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
CalendarVisibilitySwitch(
|
||||
calendarName = calendar.displayName.orUnnamed(),
|
||||
enabled = !off,
|
||||
onToggle = { on -> onSetVisible(calendar.id, on) },
|
||||
)
|
||||
@@ -149,18 +148,3 @@ internal fun HiddenCalendarsStep(
|
||||
Spacer(Modifier.height(OnboardingSpace.md))
|
||||
}
|
||||
}
|
||||
|
||||
/** The per-row switch, writing the device's own `Calendars.VISIBLE` as Settings does. */
|
||||
@Composable
|
||||
private fun CalendarSwitch(
|
||||
calendarName: String,
|
||||
enabled: Boolean,
|
||||
onToggle: (Boolean) -> Unit,
|
||||
) {
|
||||
val label = stringResource(R.string.calendars_visibility_a11y, calendarName)
|
||||
Switch(
|
||||
checked = enabled,
|
||||
onCheckedChange = onToggle,
|
||||
modifier = Modifier.semantics { contentDescription = label },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import de.jeanlucmakiola.calendula.ui.common.DurationPicker
|
||||
import de.jeanlucmakiola.calendula.ui.common.durationLabel
|
||||
import de.jeanlucmakiola.calendula.ui.common.eventFormFieldIcon
|
||||
import de.jeanlucmakiola.calendula.ui.common.eventFormFieldLabel
|
||||
import de.jeanlucmakiola.calendula.ui.common.orUnnamed
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.ExpandIcon
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
@@ -129,7 +130,7 @@ internal fun EventFormScreen(
|
||||
durationCalendars.forEachIndexed { index, calendar ->
|
||||
val override = state.perCalendarEventDuration[calendar.id]
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
title = calendar.displayName.orUnnamed(),
|
||||
summary = override?.let { durationLabel(it) }
|
||||
?: stringResource(
|
||||
R.string.settings_calendar_duration_inherits,
|
||||
|
||||
@@ -37,6 +37,7 @@ import de.jeanlucmakiola.calendula.ui.common.REMINDER_PRESETS
|
||||
import de.jeanlucmakiola.calendula.ui.common.ReminderDefaultPicker
|
||||
import de.jeanlucmakiola.calendula.ui.common.TimePickerAlert
|
||||
import de.jeanlucmakiola.calendula.ui.common.durationLabel
|
||||
import de.jeanlucmakiola.calendula.ui.common.orUnnamed
|
||||
import de.jeanlucmakiola.floret.android.hasPermission
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.ExpandIcon
|
||||
@@ -172,7 +173,7 @@ internal fun NotificationsScreen(
|
||||
// own section — link there instead.
|
||||
if (calendar.id in state.managedCalendarIds) {
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
title = calendar.displayName.orUnnamed(),
|
||||
summary = stringResource(R.string.settings_calendar_reminders_managed_hint),
|
||||
position = Position.Alone,
|
||||
leading = { CalendarColorChip(calendar.color) },
|
||||
@@ -189,7 +190,7 @@ internal fun NotificationsScreen(
|
||||
}
|
||||
val expanded = calendar.id in expandedCalendars
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
title = calendar.displayName.orUnnamed(),
|
||||
position = if (expanded) Position.Top else Position.Alone,
|
||||
leading = { CalendarColorChip(calendar.color) },
|
||||
trailing = { ExpandIcon(expanded) },
|
||||
|
||||
Reference in New Issue
Block a user