diff --git a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/addDownload/multiple/AddMultiDownloadComponent.kt b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/addDownload/multiple/AddMultiDownloadComponent.kt index 1008d45..075f9ee 100644 --- a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/addDownload/multiple/AddMultiDownloadComponent.kt +++ b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/addDownload/multiple/AddMultiDownloadComponent.kt @@ -9,8 +9,6 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import com.abdownloadmanager.desktop.pages.addDownload.multiple.AddMultiItemSaveMode.* -import com.abdownloadmanager.desktop.utils.asState import com.abdownloadmanager.utils.FileIconProvider import com.abdownloadmanager.utils.category.Category import com.abdownloadmanager.utils.category.CategoryItem @@ -68,15 +66,22 @@ class AddMultiDownloadComponent( private val categoryManager: CategoryManager by inject() val categories = categoryManager.categoriesFlow - private val _selectedCategory = MutableStateFlow(categories.value.firstOrNull()) + private val _selectedCategory = MutableStateFlow(null) val selectedCategory = _selectedCategory.asStateFlow() - fun setSelectedCategory(category: Category) { + fun setSelectedCategory(category: Category?) { _selectedCategory.update { category } } + private val _allInSameLocation = MutableStateFlow(false) + val allInSameLocation = _allInSameLocation.asStateFlow() + + fun setAllItemsInSameLocation(sameLocation: Boolean) { + _allInSameLocation.update { sameLocation } + } + fun requestAddCategory() { onRequestAddCategory() } @@ -103,12 +108,6 @@ class AddMultiDownloadComponent( } var list: List by mutableStateOf(emptyList()) - private val _saveMode = MutableStateFlow(EachFileInTheirOwnCategory) - val saveMode = _saveMode.asStateFlow() - fun setSaveMode(saveMode: AddMultiItemSaveMode) { - _saveMode.update { saveMode } - } - private val checkList = MutableSharedFlow() private fun enqueueCheck(links: List) { @@ -161,19 +160,8 @@ class AddMultiDownloadComponent( } } - val isCategoryModeHasValidState by run { - val category by selectedCategory.asState(scope) - val saveMode by saveMode.asState(scope) - derivedStateOf { - when (saveMode) { - EachFileInTheirOwnCategory -> true - AllInOneCategory -> category != null - InSameLocation -> true - } - } - } val canClickAdd by derivedStateOf { - selectionList.isNotEmpty() && isCategoryModeHasValidState + selectionList.isNotEmpty() } private val queueManager: QueueManager by inject() val queueList = queueManager.queues @@ -211,16 +199,11 @@ class AddMultiDownloadComponent( fun requestAddDownloads( queueId: Long?, ) { - val saveMode = saveMode.value - val categorySelectionMode = when (saveMode) { - EachFileInTheirOwnCategory -> CategorySelectionMode.Auto - AllInOneCategory -> selectedCategory.value?.let { - CategorySelectionMode.Fixed(it.id) - } - InSameLocation -> { - if (alsoAutoCategorize.value) CategorySelectionMode.Auto - else null + val categorySelectionMode = when { + alsoAutoCategorize.value -> CategorySelectionMode.Auto + else -> selectedCategory.value?.let { + CategorySelectionMode.Fixed(it.id) } } val itemsToAdd = list @@ -237,7 +220,7 @@ class AddMultiDownloadComponent( url = it.credentials.value.link, fleName = it.name.value, defaultFolder = it.folder.value, - allInSameLocation = saveMode == InSameLocation + allInSameLocation = allInSameLocation.value ), name = it.name.value, link = it.credentials.value.link, @@ -252,7 +235,7 @@ class AddMultiDownloadComponent( categorySelectionMode = categorySelectionMode ) val folder = folder.value - if (this.saveMode.value == InSameLocation) { + if (allInSameLocation.value) { addToLastUsedLocations(folder) } requestClose() diff --git a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/addDownload/multiple/AddMultiItemPage.kt b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/addDownload/multiple/AddMultiItemPage.kt index f93d11a..28e8126 100644 --- a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/addDownload/multiple/AddMultiItemPage.kt +++ b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/addDownload/multiple/AddMultiItemPage.kt @@ -1,45 +1,25 @@ package com.abdownloadmanager.desktop.pages.addDownload.multiple -import androidx.compose.animation.animateContentSize +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import com.abdownloadmanager.desktop.ui.theme.myTextSizes -import com.abdownloadmanager.desktop.ui.widget.ActionButton -import com.abdownloadmanager.desktop.ui.widget.Text -import com.abdownloadmanager.utils.compose.WithContentAlpha import androidx.compose.foundation.layout.* import androidx.compose.foundation.onClick -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.window.WindowDraggableArea import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset -import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.rememberDialogState -import com.abdownloadmanager.desktop.pages.addDownload.shared.* -import com.abdownloadmanager.desktop.ui.customwindow.BaseOptionDialog -import com.abdownloadmanager.desktop.ui.icon.MyIcons +import com.abdownloadmanager.desktop.pages.addDownload.shared.CategoryAddButton +import com.abdownloadmanager.desktop.pages.addDownload.shared.CategorySelect +import com.abdownloadmanager.desktop.pages.addDownload.shared.LocationTextField +import com.abdownloadmanager.desktop.pages.addDownload.shared.ShowAddToQueueDialog import com.abdownloadmanager.desktop.ui.theme.myColors -import com.abdownloadmanager.desktop.ui.util.ifThen -import com.abdownloadmanager.desktop.ui.widget.CheckBox +import com.abdownloadmanager.desktop.ui.theme.myTextSizes +import com.abdownloadmanager.desktop.ui.widget.* import com.abdownloadmanager.desktop.utils.div -import com.abdownloadmanager.desktop.utils.windowUtil.moveSafe import com.abdownloadmanager.resources.Res -import com.abdownloadmanager.resources.* -import com.abdownloadmanager.utils.compose.WithContentColor -import com.abdownloadmanager.utils.compose.widget.MyIcon +import com.abdownloadmanager.utils.category.Category +import com.abdownloadmanager.utils.compose.WithContentAlpha import ir.amirab.util.compose.resources.myStringResource -import ir.amirab.util.compose.StringSource -import ir.amirab.util.compose.asStringSource -import java.awt.MouseInfo @Composable fun AddMultiItemPage( @@ -105,10 +85,9 @@ fun Footer( verticalAlignment = Alignment.CenterVertically, ) { SaveSettings( - modifier = Modifier.width(300.dp), + modifier = Modifier.fillMaxWidth().weight(1f), component = component, ) - Spacer(Modifier.weight(1f)) Spacer(Modifier.width(8.dp)) Row(Modifier.align(Alignment.Bottom)) { ActionButton( @@ -137,324 +116,125 @@ private fun SaveSettings( modifier: Modifier, component: AddMultiDownloadComponent, ) { - Column( - modifier.animateContentSize() - ) { - var dropdownOpen by remember { mutableStateOf(false) } - val saveMode by component.saveMode.collectAsState() + val selectedCategory by component.selectedCategory.collectAsState() + + val folder by component.folder.collectAsState() + + Column(modifier) { Text("${myStringResource(Res.string.save_to)}:") Spacer(Modifier.height(8.dp)) - SaveSolution( - saveMode = saveMode, - setSaveMode = { - component.setSaveMode(it) - }, - isSelectionOpen = dropdownOpen, - setSelectionOpen = { - dropdownOpen = it - } - ) - when (saveMode) { - AddMultiItemSaveMode.EachFileInTheirOwnCategory -> { - // - } + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { + CategorySaveOption(selectedCategory, component) + Spacer(Modifier.width(8.dp)) + LocationSaveOption(component, folder) + Spacer(Modifier) + } + } +} - AddMultiItemSaveMode.AllInOneCategory -> { - Spacer(Modifier.height(8.dp)) - Row( - Modifier.height(IntrinsicSize.Max), - verticalAlignment = Alignment.CenterVertically, - ) { - CategorySelect( - categories = component.categories.collectAsState().value, - modifier = Modifier.weight(1f), - selectedCategory = component.selectedCategory.collectAsState().value, - onCategorySelected = { - component.setSelectedCategory(it) - } - ) - Spacer(Modifier.width(8.dp)) - CategoryAddButton( - Modifier.fillMaxHeight(), - enabled = true, - onClick = { - component.requestAddCategory() - }, - ) - } - } +@Composable +private fun RowScope.LocationSaveOption( + component: AddMultiDownloadComponent, + folder: String +) { + val allItemsInSameLocation by component.allInSameLocation.collectAsState() + SaveOption( + title = myStringResource(Res.string.all_items_in_one_Location), + selectedHelp = myStringResource(Res.string.all_items_in_one_Location_description), + unselectedHelp = myStringResource(Res.string.unselected_all_items_in_specific_location_description), + selected = allItemsInSameLocation, + onSelectedChange = { + component.setAllItemsInSameLocation(it) + }, + selectedContent = { + LocationTextField( + text = folder, + setText = { + component.setFolder(it) + }, + modifier = Modifier.fillMaxWidth(), + lastUsedLocations = component.lastUsedLocations.collectAsState().value + ) + } + ) +} - AddMultiItemSaveMode.InSameLocation -> { - Spacer(Modifier.height(8.dp)) - AllFilesInSameDirectory( - Modifier, - folder = component.folder.collectAsState().value, - setFolder = { - component.setFolder(it) +@Composable +private fun RowScope.CategorySaveOption( + selectedCategory: Category?, + component: AddMultiDownloadComponent +) { + + SaveOption( + title = myStringResource(Res.string.all_items_in_one_category), + selectedHelp = myStringResource(Res.string.all_items_in_one_category_description), + unselectedHelp = myStringResource(Res.string.each_item_on_its_own_category_description), + selected = selectedCategory != null, + onSelectedChange = { + if (it) { + component.setSelectedCategory(component.categories.value.firstOrNull()) + } else { + component.setSelectedCategory(null) + } + component.setAlsoAutoCategorize(!it) + }, + selectedContent = { + Row( + Modifier.height(IntrinsicSize.Max).fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + CategorySelect( + categories = component.categories.collectAsState().value, + modifier = Modifier.weight(1f), + selectedCategory = component.selectedCategory.collectAsState().value, + onCategorySelected = { + component.setSelectedCategory(it) + } + ) + Spacer(Modifier.width(8.dp)) + CategoryAddButton( + Modifier.fillMaxHeight(), + enabled = true, + onClick = { + component.requestAddCategory() }, - alsoCategorize = component.alsoAutoCategorize.collectAsState().value, - setAlsoCategorize = component::setAlsoAutoCategorize, - knownLocations = component.lastUsedLocations.collectAsState().value, ) } } - } -} - -@Composable -private fun SaveSolution( - saveMode: AddMultiItemSaveMode, - setSaveMode: (AddMultiItemSaveMode) -> Unit, - isSelectionOpen: Boolean, - setSelectionOpen: (Boolean) -> Unit, -) { - SaveSolutionHeader( - saveMode = saveMode, - onClick = { - setSelectionOpen(!isSelectionOpen) - }, ) - if (isSelectionOpen) { - SaveSolutionPopup( - selectedItem = saveMode, - onIteSelected = setSaveMode, - onDismiss = { - setSelectionOpen(false) - } - ) - } } @Composable -private fun SaveSolutionPopup( - selectedItem: AddMultiItemSaveMode, - onIteSelected: (AddMultiItemSaveMode) -> Unit, - onDismiss: () -> Unit, -) { - val state = rememberDialogState( - size = DpSize( - height = Dp.Unspecified, - width = Dp.Unspecified, - ), - ) - val close = { - onDismiss() - } - BaseOptionDialog( - onCloseRequest = close, - state = state, - resizeable = false, - ) { - LaunchedEffect(window) { - window.moveSafe( - MouseInfo.getPointerInfo().location.run { - DpOffset( - x = x.dp, - y = y.dp - ) - } - ) - } - val shape = RoundedCornerShape(6.dp) - Column( - Modifier - .clip(shape) - .border(2.dp, myColors.onBackground / 10, shape) - .background( - Brush.linearGradient( - listOf( - myColors.surface, - myColors.background, - ) - ) - ) - ) { - WithContentColor(myColors.onBackground) { - Column( - Modifier.widthIn(max = 300.dp) - ) { - WindowDraggableArea(Modifier) { - Column( - Modifier.padding(16.dp) - ) { - Text( - myStringResource(Res.string.where_should_each_item_saved), - Modifier, - fontSize = myTextSizes.base - ) - Spacer(Modifier.height(8.dp)) - WithContentAlpha(0.75f) { - Text( - myStringResource(Res.string.there_are_multiple_items_please_select_a_way_you_want_to_save_them), - Modifier, - fontSize = myTextSizes.sm, - ) - } - } - } - Column( - Modifier - .padding(horizontal = 8.dp) - .padding(bottom = 8.dp) - ) { - Spacer(Modifier.height(4.dp)) - Spacer( - Modifier.fillMaxWidth() - .height(1.dp) - .background(myColors.onBackground / 10), - ) - Spacer(Modifier.height(4.dp)) - Column { - for (item in AddMultiItemSaveMode.entries) { - SaveSolutionItem( - title = item.title.rememberString(), - description = item.description.rememberString(), - isSelected = selectedItem == item, - onClick = { - onIteSelected(item) - close() - } - ) - } - } - } - } - } - } - } -} - -@Composable -private fun SaveSolutionHeader( - saveMode: AddMultiItemSaveMode, - onClick: () -> Unit, - enabled: Boolean = true, -) { - val borderColor = myColors.onBackground / 0.1f - val background = myColors.surface / 50 - val shape = RoundedCornerShape(6.dp) - Row( - Modifier - .height(IntrinsicSize.Max) - .clip(shape) - .ifThen(!enabled) { - alpha(0.5f) - } - .border(1.dp, borderColor, shape) - .background(background) - .clickable( - enabled = enabled - ) { onClick() } - .padding(horizontal = 8.dp) - ) { - val contentModifier = Modifier - .padding(vertical = 8.dp) - .weight(1f) - Text( - saveMode.title.rememberString(), - contentModifier, - ) - Spacer( - Modifier - .padding(horizontal = 8.dp) - .fillMaxHeight().padding(vertical = 1.dp) - .width(1.dp) - .background(borderColor) - ) - MyIcon( - MyIcons.down, - null, - Modifier - .align(Alignment.CenterVertically) - .size(16.dp), - ) - } -} - -@Composable -private fun SaveSolutionItem( +private fun RowScope.SaveOption( title: String, - description: String, - isSelected: Boolean?, - onClick: () -> Unit, + selectedHelp:String, + unselectedHelp:String, + selected: Boolean, + onSelectedChange: (Boolean) -> Unit, + selectedContent: @Composable () -> Unit ) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = onClick) - .padding(8.dp) - ) { - isSelected?.let { - CheckBox(isSelected, { onClick() }, size = 12.dp) - } - Spacer(Modifier.width(8.dp)) - Column { - Text( - title, - fontSize = myTextSizes.base, - fontWeight = FontWeight.Bold - ) - Spacer(Modifier.height(4.dp)) - WithContentAlpha(0.7f) { - Text( - text = description, - fontSize = myTextSizes.sm, - modifier = Modifier + ExpandableItem( + modifier=Modifier.fillMaxWidth().weight(1f), + isExpanded = selected, + header = { + Row( + modifier=Modifier.onClick { onSelectedChange(!selected) }, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + CheckBox( + value = selected, + onValueChange = onSelectedChange ) + Text(title) + Help(if (selected) selectedHelp else unselectedHelp) + } + }, + body = { + Column { + Spacer(Modifier.height(8.dp)) + selectedContent() } } - } -} - - -@Composable -private fun AllFilesInSameDirectory( - modifier: Modifier, - folder: String, - setFolder: (String) -> Unit, - alsoCategorize: Boolean, - setAlsoCategorize: (Boolean) -> Unit, - knownLocations: List, -) { - LocationTextField( - text = folder, - setText = { - setFolder(it) - }, - modifier = modifier, - lastUsedLocations = knownLocations ) - Spacer(Modifier.height(8.dp)) - Row( - Modifier.onClick { - setAlsoCategorize(!alsoCategorize) - }, - verticalAlignment = Alignment.CenterVertically, - ) { - CheckBox( - value = alsoCategorize, - onValueChange = setAlsoCategorize - ) - Spacer(Modifier.width(4.dp)) - Text(myStringResource(Res.string.auto_categorize_downloads)) - } } - -enum class AddMultiItemSaveMode( - val title: StringSource, - val description: StringSource, -) { - EachFileInTheirOwnCategory( - title = Res.string.each_item_on_its_own_category.asStringSource(), - description = Res.string.each_item_on_its_own_category_description.asStringSource(), - ), - AllInOneCategory( - title = Res.string.all_items_in_one_category.asStringSource(), - description = Res.string.all_items_in_one_category_description.asStringSource(), - ), - InSameLocation( - title = Res.string.all_items_in_one_Location.asStringSource(), - description = Res.string.all_items_in_one_Location_description.asStringSource(), - ); -} \ No newline at end of file diff --git a/shared/resources/src/main/resources/com/abdownloadmanager/resources/locales/en_US.properties b/shared/resources/src/main/resources/com/abdownloadmanager/resources/locales/en_US.properties index 6d77bf5..258b295 100644 --- a/shared/resources/src/main/resources/com/abdownloadmanager/resources/locales/en_US.properties +++ b/shared/resources/src/main/resources/com/abdownloadmanager/resources/locales/en_US.properties @@ -110,9 +110,10 @@ there_are_multiple_items_please_select_a_way_you_want_to_save_them=There are mul each_item_on_its_own_category=Each item on its own category each_item_on_its_own_category_description=Each item will be placed in a category that have that file type all_items_in_one_category=All items in one Category -all_items_in_one_category_description=All files will be saved in the selected category location +all_items_in_one_category_description=All files will be saved in the selected category all_items_in_one_Location=All items in one Location all_items_in_one_Location_description=All items will be saved in the selected directory +unselected_all_items_in_specific_location_description=All files will be saved in the selected category location no_category_selected=No Category Selected download_location=Download Location location=Location