feat: pin import action to the top bar, fold count into title
Move the multi-event import's confirm button into the app-bar actions so
it's reachable without scrolling past a long calendar list, and put the
count in the title ('Importing 5 events') instead of a separate 'N events
in this file' line. Hoists the selected target calendar to the screen so
the top-bar action can read it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -85,18 +85,52 @@ fun ImportScreen(
|
|||||||
(state as? ImportUiState.Single)?.let { onOpenSingle(it.form); onClose() }
|
(state as? ImportUiState.Single)?.let { onOpenSingle(it.form); onClose() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hoisted target calendar so the always-visible top-bar Import action can
|
||||||
|
// read it without the user scrolling to a bottom button. Re-defaults to the
|
||||||
|
// first calendar when the "many" list first arrives (keyed on it), then
|
||||||
|
// holds the user's pick.
|
||||||
|
val many = state as? ImportUiState.Many
|
||||||
|
var selected by rememberSaveable(many?.calendars?.firstOrNull()?.id) {
|
||||||
|
mutableStateOf(many?.calendars?.firstOrNull()?.id)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.predictiveBack(onBack = onClose)
|
.predictiveBack(onBack = onClose)
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(stringResource(R.string.import_title)) },
|
title = {
|
||||||
|
Text(
|
||||||
|
if (many != null) {
|
||||||
|
pluralStringResource(
|
||||||
|
R.plurals.import_title_count,
|
||||||
|
many.events.size,
|
||||||
|
many.events.size,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
stringResource(R.string.import_title)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onClose) {
|
IconButton(onClick = onClose) {
|
||||||
Icon(Icons.Default.Close, contentDescription = stringResource(R.string.event_edit_close))
|
Icon(Icons.Default.Close, contentDescription = stringResource(R.string.event_edit_close))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
actions = {
|
||||||
|
// Only meaningful in the multi-event picker with a writable
|
||||||
|
// target; every other state has nothing to confirm here.
|
||||||
|
if (many != null && many.calendars.isNotEmpty()) {
|
||||||
|
Button(
|
||||||
|
onClick = { selected?.let(viewModel::import) },
|
||||||
|
enabled = selected != null,
|
||||||
|
modifier = Modifier.padding(end = 12.dp),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.import_button))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
),
|
),
|
||||||
@@ -112,7 +146,7 @@ fun ImportScreen(
|
|||||||
|
|
||||||
ImportUiState.Empty -> CenteredMessage(stringResource(R.string.import_empty), onClose)
|
ImportUiState.Empty -> CenteredMessage(stringResource(R.string.import_empty), onClose)
|
||||||
ImportUiState.Failed -> CenteredMessage(stringResource(R.string.import_failed), onClose)
|
ImportUiState.Failed -> CenteredMessage(stringResource(R.string.import_failed), onClose)
|
||||||
is ImportUiState.Many -> ManyContent(s, onImport = viewModel::import)
|
is ImportUiState.Many -> ManyContent(s, selected, onSelect = { selected = it })
|
||||||
is ImportUiState.Done -> DoneContent(s, onClose)
|
is ImportUiState.Done -> DoneContent(s, onClose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,27 +154,21 @@ fun ImportScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ManyContent(state: ImportUiState.Many, onImport: (Long) -> Unit) {
|
private fun ManyContent(state: ImportUiState.Many, selected: Long?, onSelect: (Long) -> Unit) {
|
||||||
// No writable calendar to import into — tell the user honestly.
|
// No writable calendar to import into — tell the user honestly.
|
||||||
if (state.calendars.isEmpty()) {
|
if (state.calendars.isEmpty()) {
|
||||||
CenteredMessage(stringResource(R.string.import_no_calendar), onClose = null)
|
CenteredMessage(stringResource(R.string.import_no_calendar), onClose = null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var selected by rememberSaveable { mutableStateOf(state.calendars.first().id) }
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier.fillMaxSize().verticalScroll(rememberScrollState())
|
Modifier.fillMaxSize().verticalScroll(rememberScrollState())
|
||||||
.padding(vertical = 8.dp),
|
.padding(top = 8.dp, bottom = 24.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
|
||||||
pluralStringResource(R.plurals.import_event_count, state.events.size, state.events.size),
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 8.dp, bottom = 12.dp),
|
|
||||||
)
|
|
||||||
CalendarPickerGroups(
|
CalendarPickerGroups(
|
||||||
calendars = state.calendars,
|
calendars = state.calendars,
|
||||||
selectedId = selected,
|
selectedId = selected,
|
||||||
onSelect = { selected = it },
|
onSelect = onSelect,
|
||||||
)
|
)
|
||||||
if (state.warnings.isNotEmpty()) {
|
if (state.warnings.isNotEmpty()) {
|
||||||
Column(
|
Column(
|
||||||
@@ -150,12 +178,6 @@ private fun ManyContent(state: ImportUiState.Many, onImport: (Long) -> Unit) {
|
|||||||
state.warnings.forEach { WarningText(it) }
|
state.warnings.forEach { WarningText(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button(
|
|
||||||
onClick = { onImport(selected) },
|
|
||||||
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 16.dp, top = 8.dp),
|
|
||||||
) {
|
|
||||||
Text(pluralStringResource(R.plurals.import_action, state.events.size, state.events.size))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -473,6 +473,11 @@
|
|||||||
<string name="import_warning_no_start">An event without a start time was skipped.</string>
|
<string name="import_warning_no_start">An event without a start time was skipped.</string>
|
||||||
<string name="import_warning_attendees">Guest lists weren\'t imported.</string>
|
<string name="import_warning_attendees">Guest lists weren\'t imported.</string>
|
||||||
<string name="import_warning_timezone">An unknown time zone fell back to your device\'s.</string>
|
<string name="import_warning_timezone">An unknown time zone fell back to your device\'s.</string>
|
||||||
|
<string name="import_button">Import</string>
|
||||||
|
<plurals name="import_title_count">
|
||||||
|
<item quantity="one">Importing %d event</item>
|
||||||
|
<item quantity="other">Importing %d events</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="import_event_count">
|
<plurals name="import_event_count">
|
||||||
<item quantity="one">%d event in this file.</item>
|
<item quantity="one">%d event in this file.</item>
|
||||||
<item quantity="other">%d events in this file.</item>
|
<item quantity="other">%d events in this file.</item>
|
||||||
|
|||||||
Reference in New Issue
Block a user