Skip to content

Commit 081f75d

Browse files
committed
Handle folder selection errors #1
1 parent 59dd1e8 commit 081f75d

9 files changed

Lines changed: 56 additions & 9 deletions

File tree

shared/src/androidMain/kotlin/com/module/notelycompose/MainActivity.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,15 @@ class MainActivity : AppCompatActivity() {
5656
private fun setupFolderPickerLauncher() {
5757
folderPickerLauncherHolder.folderPickerLauncher =
5858
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri: Uri? ->
59-
uri?.let {
59+
if (uri != null) {
6060
contentResolver.takePersistableUriPermission(
6161
uri,
6262
android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION or
6363
android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
6464
)
65-
folderPickerLauncherHolder.onFolderSelected?.invoke(uri)
6665
}
66+
// Always invoke callback, even if uri is null
67+
folderPickerLauncherHolder.onFolderSelected?.invoke(uri)
6768
}
6869
}
6970

shared/src/androidMain/kotlin/com/module/notelycompose/PermissionLauncherHolder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ class FileSaverHandler(
1919

2020
class FolderPickerLauncherHolder {
2121
var folderPickerLauncher: ActivityResultLauncher<Uri?>? = null
22-
var onFolderSelected: ((Uri) -> Unit)? = null
22+
var onFolderSelected: ((Uri?) -> Unit)? = null
2323
}
2424

2525
class FolderPickerHandler(
2626
private val folderPickerLauncherHolder: FolderPickerLauncherHolder
2727
) {
28-
fun pickFolder(onFolderSelected: (Uri) -> Unit) {
28+
fun pickFolder(onFolderSelected: (Uri?) -> Unit) {
2929
folderPickerLauncherHolder.onFolderSelected = onFolderSelected
3030
folderPickerLauncherHolder.folderPickerLauncher?.launch(null)
3131
}

shared/src/androidMain/kotlin/com/module/notelycompose/export/domain/ExportSelectionInteractorImpl.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ class ExportSelectionInteractorImpl(
3535
onResult: (Result<String>) -> Unit
3636
) {
3737
folderPickerHandler.pickFolder { folderUri ->
38+
39+
if (folderUri == null) {
40+
onResult(Result.failure(NoFolderSelectedException("Folder selection cancelled")))
41+
return@pickFolder
42+
}
43+
3844
kotlinx.coroutines.GlobalScope.launch(Dispatchers.IO) {
3945
val result = performTextExport(
4046
folderUri,

shared/src/commonMain/composeResources/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
<string name="batch_export_settings_how_to_2">2.</string>
208208
<string name="batch_export_settings_how_to_3">3.</string>
209209
<string name="batch_export_settings_error_occurred">error occurred while exporting</string>
210+
<string name="batch_export_settings_no_folder_selected">Folder selection cancelled</string>
210211
<!-- Import -->
211212
<string name="import_failed_title">Import Failed</string>
212213
</resources>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.module.notelycompose.export.domain
2+
3+
class NoFolderSelectedException(message: String): Exception()

shared/src/commonMain/kotlin/com/module/notelycompose/export/presentation/ExportSelectionViewModel.kt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import androidx.lifecycle.viewModelScope
55
import audio.utils.deleteFile
66
import com.module.notelycompose.audio.ui.importing.ImportingAudioState
77
import com.module.notelycompose.export.domain.ExportSelectionInteractor
8+
import com.module.notelycompose.export.domain.NoFolderSelectedException
89
import com.module.notelycompose.export.presentation.model.ExportSelectionPresentationState
910
import com.module.notelycompose.export.presentation.model.ExportingFileState
1011
import com.module.notelycompose.notes.domain.GetAllNotesUseCase
1112
import com.module.notelycompose.notes.presentation.list.NoteListPresentationState
1213
import com.module.notelycompose.notes.presentation.mapper.NotePresentationMapper
14+
import kotlinx.coroutines.Delay
15+
import kotlinx.coroutines.delay
1316
import kotlinx.coroutines.flow.MutableStateFlow
1417
import kotlinx.coroutines.flow.StateFlow
1518
import kotlinx.coroutines.flow.combine
@@ -19,6 +22,7 @@ import kotlinx.coroutines.flow.map
1922
import kotlinx.coroutines.flow.onEach
2023
import kotlinx.coroutines.flow.update
2124
import kotlinx.coroutines.launch
25+
import kotlin.time.Duration.Companion.seconds
2226

2327
class ExportSelectionViewModel(
2428
private val getAllNotesUseCase: GetAllNotesUseCase,
@@ -78,6 +82,10 @@ class ExportSelectionViewModel(
7882
val texts = _state.value.selectedNotes.map { it.content }
7983
val titles = _state.value.selectedNotes.map { it.title }
8084
val audioPath = _state.value.selectedNotes.map { it.recordingPath }
85+
viewModelScope.launch {
86+
delay(2.seconds)
87+
_exportingState.update { ExportingFileState.Exporting() }
88+
}
8189
exportSelectionInteractor.exportAllSelection(
8290
texts = texts,
8391
titles = titles,
@@ -91,9 +99,16 @@ class ExportSelectionViewModel(
9199
_exportingState.update {
92100
when {
93101
result.isSuccess -> ExportingFileState.Success
94-
else -> ExportingFileState.Failure(
95-
result.exceptionOrNull()?.message ?: "Export failed"
96-
)
102+
else -> {
103+
_exportingState.update { ExportingFileState.Idle }
104+
if(result.exceptionOrNull() is NoFolderSelectedException) {
105+
ExportingFileState.NoFolderSelected
106+
} else {
107+
ExportingFileState.Failure(
108+
result.exceptionOrNull()?.message ?: "Export failed"
109+
)
110+
}
111+
}
97112
}
98113
}
99114
}

shared/src/commonMain/kotlin/com/module/notelycompose/export/presentation/model/ExportingFileState.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ sealed interface ExportingFileState {
55
data class Exporting(val progress: Float = 0f) : ExportingFileState
66
object Success : ExportingFileState
77
data class Failure(val message: String) : ExportingFileState
8+
object NoFolderSelected : ExportingFileState
89
}

shared/src/commonMain/kotlin/com/module/notelycompose/export/ui/ExportingFileStateHost.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ import com.module.notelycompose.export.presentation.model.ExportingFileState
2626
import com.module.notelycompose.notes.ui.theme.LocalCustomColors
2727
import com.module.notelycompose.resources.Res
2828
import com.module.notelycompose.resources.batch_export_settings_error_occurred
29+
import com.module.notelycompose.resources.batch_export_settings_no_folder_selected
2930
import com.module.notelycompose.resources.batch_export_ok
31+
import com.module.notelycompose.resources.error
3032
import kotlinx.coroutines.delay
3133
import org.jetbrains.compose.resources.stringResource
3234

@@ -65,11 +67,29 @@ internal fun ExportingFileStateHost(
6567
Text(stringResource(Res.string.batch_export_ok))
6668
}
6769
},
68-
title = { Text(stringResource(Res.string.batch_export_ok)) },
70+
title = { Text(stringResource(Res.string.error)) },
6971
text = { Text(stringResource(Res.string.batch_export_settings_error_occurred)) }
7072
)
7173
}
7274
}
75+
76+
is ExportingFileState.NoFolderSelected -> {
77+
if(shouldShowDialog) {
78+
AlertDialog(
79+
onDismissRequest = onDismiss,
80+
confirmButton = {
81+
TextButton(onClick = {
82+
shouldShowDialog = false
83+
onDismiss()
84+
}) {
85+
Text(stringResource(Res.string.batch_export_ok))
86+
}
87+
},
88+
title = { Text(stringResource(Res.string.error)) },
89+
text = { Text(stringResource(Res.string.batch_export_settings_no_folder_selected)) }
90+
)
91+
}
92+
}
7393
}
7494
}
7595

shared/src/iosMain/kotlin/com/module/notelycompose/export/domain/ExportSelectionInteractorImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class ExportSelectionInteractorImpl : ExportSelectionInteractor {
3535
val folderUrl = pickFolder()
3636

3737
if (folderUrl == null) {
38-
onResult(Result.failure(Exception("Folder selection cancelled")))
38+
onResult(Result.failure(NoFolderSelectedException("Folder selection cancelled")))
3939
return@launch
4040
}
4141

0 commit comments

Comments
 (0)