release: v2.14.0 — Day view on date-header tap #61
@@ -172,9 +172,15 @@ fun CalendarHost(
|
||||
// picker (many). A plain conditional overlay (no slide) — it's transient.
|
||||
var importUri by remember { mutableStateOf<android.net.Uri?>(null) }
|
||||
var importForm by remember { mutableStateOf<EventForm?>(null) }
|
||||
// A restore (in-app "Restore from .ics" button) always runs the full import
|
||||
// flow — picker + summary — even for a single-event file, because the intent
|
||||
// is "restore a backup", not "add this one event". An externally opened .ics
|
||||
// keeps routing a single event straight into the prefilled create form.
|
||||
var importForceMany by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(requestedImportUri) {
|
||||
if (requestedImportUri != null) {
|
||||
importUri = requestedImportUri
|
||||
importForceMany = false
|
||||
onImportConsumed()
|
||||
}
|
||||
}
|
||||
@@ -414,7 +420,7 @@ fun CalendarHost(
|
||||
) {
|
||||
CalendarsScreen(
|
||||
onBack = { showCalendars = false },
|
||||
onImport = { importUri = it },
|
||||
onImport = { importUri = it; importForceMany = true },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -423,6 +429,7 @@ fun CalendarHost(
|
||||
importUri?.let { uri ->
|
||||
ImportScreen(
|
||||
uri = uri,
|
||||
forceMany = importForceMany,
|
||||
onClose = { importUri = null },
|
||||
onOpenSingle = { form ->
|
||||
importUri = null
|
||||
|
||||
@@ -64,7 +64,9 @@ import de.jeanlucmakiola.calendula.ui.common.predictiveBack
|
||||
* Handles an opened/received `.ics` file. A single event is handed straight to
|
||||
* the prefilled create form via [onOpenSingle]; several events show a target-
|
||||
* calendar picker and import in bulk (dedup by UID), then a result summary.
|
||||
* Empty/failed files show a short message and close.
|
||||
* Empty/failed files show a short message and close. [forceMany] keeps a
|
||||
* single-event file on the bulk path — used by the in-app restore, whose intent
|
||||
* is "restore a backup" rather than "add this one event".
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -72,9 +74,10 @@ fun ImportScreen(
|
||||
uri: Uri,
|
||||
onClose: () -> Unit,
|
||||
onOpenSingle: (EventForm) -> Unit,
|
||||
forceMany: Boolean = false,
|
||||
viewModel: ImportViewModel = hiltViewModel(),
|
||||
) {
|
||||
LaunchedEffect(uri) { viewModel.load(uri) }
|
||||
LaunchedEffect(uri) { viewModel.load(uri, forceMany) }
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
// A single event isn't shown here — it opens the create form for review.
|
||||
|
||||
@@ -66,8 +66,13 @@ class ImportViewModel @Inject constructor(
|
||||
val state: StateFlow<ImportUiState> = _state.asStateFlow()
|
||||
private var started = false
|
||||
|
||||
/** Read + parse [uri] once; subsequent calls (recomposition) are ignored. */
|
||||
fun load(uri: Uri) {
|
||||
/**
|
||||
* Read + parse [uri] once; subsequent calls (recomposition) are ignored.
|
||||
* When [forceMany] is set (an in-app restore), a single-event file still goes
|
||||
* through the bulk picker + summary rather than the prefilled create form —
|
||||
* a restore is "bring back a backup", not "add this one event".
|
||||
*/
|
||||
fun load(uri: Uri, forceMany: Boolean = false) {
|
||||
if (started) return
|
||||
started = true
|
||||
viewModelScope.launch {
|
||||
@@ -77,7 +82,7 @@ class ImportViewModel @Inject constructor(
|
||||
_state.value = when {
|
||||
parsed == null -> ImportUiState.Failed
|
||||
parsed.events.isEmpty() -> ImportUiState.Empty
|
||||
parsed.events.size == 1 -> ImportUiState.Single(
|
||||
parsed.events.size == 1 && !forceMany -> ImportUiState.Single(
|
||||
form = parsed.events.single().toEventForm(TimeZone.currentSystemDefault()),
|
||||
warnings = parsed.warnings,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user