Skip to content

fix(sync): recover missing remote destinations#197

Merged
veryCrunchy merged 3 commits into
mainfrom
fix/192-missing-remote-destination
Jul 24, 2026
Merged

fix(sync): recover missing remote destinations#197
veryCrunchy merged 3 commits into
mainfrom
fix/192-missing-remote-destination

Conversation

@veryCrunchy

Copy link
Copy Markdown
Member

Summary

  • expose typed WebDAV folder-listing status failures on Android and desktop
  • recover missing suggested destinations to the nearest network-confirmed parent
  • create all missing nested segments only after explicit picker confirmation
  • retain picker path state across recreation and preserve cache, cancellation, and path-safety behavior

Validation

  • :ui:desktopTest focused picker and desktop status tests
  • :androidApp:testDebugUnitTest focused Android status test
  • :androidApp:assembleDebug
  • repository hygiene and diff checks

Closes #192

Copilot AI review requested due to automatic review settings July 24, 2026 21:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the remote destination picker’s resilience when a suggested Nextcloud sync destination doesn’t exist yet by surfacing typed WebDAV listing failures (HTTP status) across Android and desktop, and using that to recover to an accessible parent and offer explicit, confirmed folder creation.

Changes:

  • Introduce NextcloudFileListingHttpException and throw it on non-success WebDAV folder listing responses (Android + desktop).
  • Add shared UI recovery logic to walk up ancestors on typed 404s and present an explicit “create and use” flow for missing suggested destinations.
  • Preserve picker state across recreation via rememberSaveable, and extend tests to cover the new recovery and typed-error behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ui/src/desktopTest/kotlin/dev/obiente/nextcloudnative/app/NextcloudFileListingFailureTest.kt Adds a desktop test asserting typed HTTP status exposure.
ui/src/desktopMain/kotlin/dev/obiente/nextcloudnative/app/DesktopNextcloudServices.kt Throws typed listing exception instead of generic error on non-207 responses.
ui/src/commonTest/kotlin/dev/obiente/nextcloudnative/app/RemoteFolderPickerTest.kt Adds tests for missing-destination recovery, typed 404-only behavior, and UI status messaging.
ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/RemoteFolderPicker.kt Implements missing suggested destination recovery + confirmed nested creation; persists dialog state across recreation.
ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/NextcloudFileListing.kt Introduces typed HTTP exception for folder listing failures.
androidApp/src/test/kotlin/dev/obiente/nextcloudnative/NextcloudFileListingFailureTest.kt Adds an Android unit test asserting typed HTTP status exposure.
androidApp/src/main/kotlin/dev/obiente/nextcloudnative/AndroidNextcloudServices.kt Throws typed listing exception instead of generic error on non-207 responses.
Comments suppressed due to low confidence (1)

ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/RemoteFolderPicker.kt:271

  • missingDestination is not reset when a new load starts, so a previously computed missing-destination banner/status can persist while showing cached results or after a network failure. This can mislead users (e.g., it can still say the destination will be created even though the current listing isn't network-confirmed and the confirm button is disabled). Clear missingDestination alongside the other per-load state resets so it's only shown after a successful network listing recomputes it.
    LaunchedEffect(session, userId, currentPath, loadAttempt) {
        files = null
        listingSource = null
        networkConfirmedPath = null
        loading = true
        refreshing = false
        error = null
        query = ""
        val cached = runCatching {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +301 to +303
val recoveryParent = recoveryTarget?.let {
missingRemoteFolderParentAfter(failure, currentPath)
}

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Comment on lines +679 to +692
scope.launch {
runCatching {
destination.pathsToCreate.forEach { path ->
services.createDirectoryIfAbsent(session, userId, path)
}
}.rethrowRemoteFolderCancellation().onSuccess {
recoveryTarget = null
missingDestination = null
onSelected(destination.intendedPath)
}.onFailure { failure ->
createError = failure.message ?: "Could not create this destination."
}
createRunning = false
}
Comment on lines +667 to +670
enabled = canConfirm || (
!createRunning &&
missingDestination?.accessibleParentPath == networkConfirmedPath
),
@veryCrunchy
veryCrunchy merged commit a5d9c49 into main Jul 24, 2026
2 checks passed
@veryCrunchy veryCrunchy added priority:P0 Required for a trustworthy mobile replacement or prevents data loss area:files-sync Files, offline storage, transfer, and synchronization platform:android Android-specific implementation or validation type:bug labels Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:files-sync Files, offline storage, transfer, and synchronization platform:android Android-specific implementation or validation priority:P0 Required for a trustworthy mobile replacement or prevents data loss type:bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(sync): recover when a suggested Nextcloud destination does not exist

2 participants