make category path optional

This commit is contained in:
AmirHossein Abdolmotallebi 2024-11-03 11:38:57 +03:30
parent 9e319f6bb7
commit f91d79765b
7 changed files with 74 additions and 26 deletions

View File

@ -192,13 +192,13 @@ class AddMultiDownloadComponent(
url = url,
fileName = fleName,
)
)?.path
)?.getDownloadPath()
?: defaultFolder
}
is CategorySelectionMode.Fixed -> {
downloadSystem.categoryManager
.getCategoryById(categorySelectionMode.categoryId)?.path
.getCategoryById(categorySelectionMode.categoryId)?.getDownloadPath()
?: defaultFolder
}

View File

@ -11,7 +11,6 @@ import androidx.compose.runtime.*
import com.abdownloadmanager.desktop.utils.mvi.ContainsEffects
import com.abdownloadmanager.desktop.utils.mvi.supportEffects
import com.abdownloadmanager.resources.Res
import com.abdownloadmanager.resources.*
import com.abdownloadmanager.utils.extractors.linkextractor.DownloadCredentialFromStringExtractor
import com.arkivanov.decompose.ComponentContext
import ir.amirab.downloader.connection.DownloaderClient
@ -66,20 +65,29 @@ class AddSingleDownloadComponent(
private val _useCategory = MutableStateFlow(false)
val useCategory = _useCategory.asStateFlow()
fun setUseCategory(value: Boolean) {
_useCategory.update { value }
if (value) {
useCategoryFolder()
fun setUseCategory(useCategory: Boolean) {
_useCategory.update { useCategory }
if (useCategory) {
val usedCategoryFolder = useCategoryFolder(_useCategory.value)
if (!usedCategoryFolder) {
useDefaultFolder()
}
} else {
useDefaultFolder()
}
}
private fun useCategoryFolder() {
private fun useCategoryFolder(
useCategory: Boolean,
): Boolean {
val category = selectedCategory.value
if (useCategory.value && category != null) {
setFolder(category.path)
if (useCategory && category != null) {
category.getDownloadPath()?.let {
setFolder(it)
return true
}
}
return false
}
private fun useDefaultFolder() {
@ -89,8 +97,12 @@ class AddSingleDownloadComponent(
fun setSelectedCategory(category: Category) {
_selectedCategory.update { category }
if (useCategory.value) {
useCategoryFolder()
val useCategory = useCategory.value
if (useCategory) {
val used = useCategoryFolder(useCategory)
if (!used) {
useDefaultFolder()
}
}
}
@ -145,8 +157,8 @@ class AddSingleDownloadComponent(
if (category == null) {
setUseCategory(false)
} else {
setUseCategory(true)
setSelectedCategory(category)
setUseCategory(true)
}
}.launchIn(scope)
}
@ -333,7 +345,7 @@ class AddSingleDownloadComponent(
true
} else {
// only add if category path is not the same as provided path
category.path != folder
category.getDownloadPath() != folder
}
if (shouldAdd) {
addToLastUsedLocations(folder)

View File

@ -42,6 +42,7 @@ class CategoryComponent(
setUrlPatternsEnabled(category.acceptedUrlPatterns.isNotEmpty())
setUrlPatterns(category.acceptedUrlPatterns.joinToString(" "))
setPath(category.path)
setUsePath(category.usePath)
}
}
@ -81,15 +82,22 @@ class CategoryComponent(
_path.value = path
}
private val _usePath = MutableStateFlow(false)
val usePath = _usePath.asStateFlow()
fun setUsePath(usePath: Boolean) {
_usePath.value = usePath
}
val canSubmit = combineStateFlows(
icon,
name,
types,
path
) { icon, name, types, path ->
path,
usePath,
) { icon, name, types, path, usePath ->
val iconOk = icon != null
val nameOk = name.isNotBlank()
val pathOk = FileUtils.canWriteInThisFolder(path)
val pathOk = FileUtils.canWriteInThisFolder(path) || !usePath
iconOk && nameOk && pathOk
}
val isEditMode = id >= 0
@ -114,6 +122,7 @@ class CategoryComponent(
.value!!
.uriOrNull()!!,
path = path,
usePath = usePath.value,
acceptedUrlPatterns = urlPatterns.value
.split(" ")
.filterNot { it.isBlank() }

View File

@ -82,7 +82,9 @@ fun NewCategory(
CategoryDefaultPath(
path = categoryComponent.path.collectAsState().value,
onPathChanged = categoryComponent::setPath,
defaultDownloadLocation = categoryComponent.defaultDownloadLocation.collectAsState().value
defaultDownloadLocation = categoryComponent.defaultDownloadLocation.collectAsState().value,
checked = categoryComponent.usePath.collectAsState().value,
setChecked = categoryComponent::setUsePath
)
}
Spacer(Modifier.height(12.dp))
@ -115,6 +117,8 @@ fun CategoryDefaultPath(
defaultDownloadLocation: String,
path: String,
onPathChanged: (String) -> Unit,
checked: Boolean,
setChecked: (Boolean) -> Unit,
) {
val initialDirectory = remember(path, defaultDownloadLocation) {
path
@ -135,14 +139,17 @@ fun CategoryDefaultPath(
directory?.path?.let(onPathChanged)
}
WithLabel(
OptionalWithLabel(
label = myStringResource(Res.string.category_download_location),
helpText = myStringResource(Res.string.category_download_location_description)
helpText = myStringResource(Res.string.category_download_location_description),
enabled = checked,
setEnabled = setChecked,
) {
CategoryPageTextField(
text = path,
onTextChange = onPathChanged,
modifier = Modifier.fillMaxWidth(),
enabled = checked,
placeholder = "",
errorText = null,
end = {

View File

@ -289,6 +289,7 @@ class CategoryActions(
private val onRequestAddCategory: () -> Unit,
) {
private val mainItemExists = MutableStateFlow(categoryItem != null)
private val canBeOpened = MutableStateFlow(categoryItem?.usePath ?: false)
private inline fun useItem(
block: (Category) -> Unit,
) {
@ -298,7 +299,7 @@ class CategoryActions(
val openCategoryFolderAction = simpleAction(
title = Res.string.open_folder.asStringSource(),
icon = MyIcons.folderOpen,
checkEnable = mainItemExists,
checkEnable = canBeOpened,
onActionPerformed = {
scope.launch {
useItem {
@ -843,7 +844,9 @@ class HomeComponent(
categoryItem = categoryItem,
openFolder = {
runCatching {
FileUtils.openFolder(File(it.path))
it.getDownloadPath()?.let {
FileUtils.openFolder(File(it))
}
}
},
onRequestAddCategory = {

View File

@ -6,6 +6,7 @@ import androidx.compose.runtime.remember
import ir.amirab.util.compose.IconSource
import ir.amirab.util.compose.fromUri
import ir.amirab.util.wildcardMatch
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
@ -17,13 +18,23 @@ import kotlinx.serialization.Serializable
@Immutable
@Serializable
data class Category(
@SerialName("id")
val id: Long,
@SerialName("name")
val name: String,
@SerialName("icon")
val icon: String,
@SerialName("path")
// don't directly use this check for usePath first! see [getDownloadPath()]
val path: String,
@SerialName("usePath")
val usePath: Boolean = true,
@SerialName("acceptedFileTypes")
val acceptedFileTypes: List<String> = emptyList(),
// this is optional if nothing provided it means that every url is acceptable
@SerialName("acceptedUrlPatterns")
val acceptedUrlPatterns: List<String> = emptyList(),
@SerialName("items")
val items: List<Long> = emptyList(),
) {
val hasUrlPattern = acceptedUrlPatterns.isNotEmpty()
@ -42,7 +53,9 @@ data class Category(
items = items.plus(newItems).distinct()
)
}
fun getDownloadPath(): String? {
return if (usePath) path else null
}
fun acceptUrl(url: String): Boolean {
if (!hasUrlPattern) {
return true

View File

@ -133,8 +133,12 @@ class CategoryManager(
private fun createDirectoryIfNecessary(category: Category) {
kotlin.runCatching {
val folder = File(category.path)
if (folder.exists()) {
val folder = category
.getDownloadPath()
?.let(::File)
?.canonicalFile
?: return
if (!folder.exists()) {
folder.mkdirs()
}
}
@ -201,7 +205,7 @@ class CategoryManager(
fun isThisPathBelongsToACategory(folder: String): Boolean {
return getCategories()
.map { it.path }.contains(folder)
.mapNotNull { it.getDownloadPath() }.contains(folder)
}
companion object {