diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d6bf85ae4..c856f2f0a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
- [FIX] Fix empty response in faphub category
- [FIX] New file manager uploading progress
- [FIX] Fix build when no metrics enabled
+- [Feature] Add count subfolders for new file manager
# 1.8.0
Attention: don't forget to add the flag for F-Droid before release
diff --git a/components/core/ui/ktx/src/commonMain/kotlin/com/flipperdevices/core/ui/ktx/PlaceholderKtx.kt b/components/core/ui/ktx/src/commonMain/kotlin/com/flipperdevices/core/ui/ktx/PlaceholderKtx.kt
index d4e85c52e0..46c66c95a2 100644
--- a/components/core/ui/ktx/src/commonMain/kotlin/com/flipperdevices/core/ui/ktx/PlaceholderKtx.kt
+++ b/components/core/ui/ktx/src/commonMain/kotlin/com/flipperdevices/core/ui/ktx/PlaceholderKtx.kt
@@ -14,10 +14,13 @@ import io.github.fornewid.placeholder.foundation.placeholder
import io.github.fornewid.placeholder.foundation.shimmer
@Suppress("ModifierComposed") // MOB-1039
-fun Modifier.placeholderConnecting(shape: Int = 4) = composed {
+fun Modifier.placeholderConnecting(
+ shape: Int = 4,
+ visible: Boolean = true
+) = composed {
this.then(
placeholder(
- visible = true,
+ visible = visible,
shape = RoundedCornerShape(shape.dp),
color = LocalPallet.current.placeholder.copy(alpha = 0.2f),
highlight = PlaceholderHighlight.shimmer(
diff --git a/components/filemngr/listing/impl/src/commonMain/composeResources/values/strings.xml b/components/filemngr/listing/impl/src/commonMain/composeResources/values/strings.xml
index 352e96d6e9..4463a08004 100644
--- a/components/filemngr/listing/impl/src/commonMain/composeResources/values/strings.xml
+++ b/components/filemngr/listing/impl/src/commonMain/composeResources/values/strings.xml
@@ -23,6 +23,7 @@
File Manager
+ %1$s items
No Files Yet
Upload Files
Select All
diff --git a/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/composable/LoadedFilesComposable.kt b/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/composable/LoadedFilesComposable.kt
index 2a196f0c18..fb10cf83ef 100644
--- a/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/composable/LoadedFilesComposable.kt
+++ b/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/composable/LoadedFilesComposable.kt
@@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier
import com.flipperdevices.bridge.connection.feature.storage.api.model.FileType
import com.flipperdevices.core.ktx.jre.toFormattedSize
import com.flipperdevices.core.preference.pb.FileManagerOrientation
+import com.flipperdevices.filemanager.listing.impl.model.ExtendedListingItem
import com.flipperdevices.filemanager.listing.impl.model.PathWithType
import com.flipperdevices.filemanager.listing.impl.viewmodel.DeleteFilesViewModel
import com.flipperdevices.filemanager.listing.impl.viewmodel.FilesViewModel
@@ -19,9 +20,12 @@ import com.flipperdevices.filemanager.ui.components.itemcard.FolderCardPlacehold
import com.flipperdevices.filemanager.ui.components.itemcard.components.asPainter
import com.flipperdevices.filemanager.ui.components.itemcard.components.asTint
import com.flipperdevices.filemanager.ui.components.itemcard.model.ItemUiSelectionState
+import flipperapp.components.filemngr.listing.impl.generated.resources.fml_items_in_folder
import okio.Path
+import org.jetbrains.compose.resources.stringResource
+import flipperapp.components.filemngr.listing.impl.generated.resources.Res as FML
-@Suppress("FunctionNaming", "LongParameterList")
+@Suppress("FunctionNaming", "LongParameterList", "LongMethod")
fun LazyGridScope.LoadedFilesComposable(
path: Path,
deleteFileState: DeleteFilesViewModel.State,
@@ -37,7 +41,7 @@ fun LazyGridScope.LoadedFilesComposable(
) {
items(filesState.files) { file ->
val isFileLoading = remember(deleteFileState.fileNamesOrNull) {
- deleteFileState.fileNamesOrNull.orEmpty().contains(file.fileName)
+ deleteFileState.fileNamesOrNull.orEmpty().contains(file.itemName)
}
Crossfade(isFileLoading) { animatedIsFileLoading ->
if (animatedIsFileLoading) {
@@ -49,27 +53,37 @@ fun LazyGridScope.LoadedFilesComposable(
orientation = orientation,
)
} else {
- val filePathWithType = remember(path, file.fileName) {
- val fullPath = path.resolve(file.fileName)
- PathWithType(file.fileType ?: FileType.FILE, fullPath)
+ val filePathWithType = remember(path, file.itemName) {
+ val fullPath = path.resolve(file.itemName)
+ PathWithType(file.itemType, fullPath)
}
FolderCardComposable(
modifier = Modifier
.fillMaxWidth()
.animateItem()
.animateContentSize(),
- painter = file.asPainter(),
- iconTint = file.asTint(),
- title = file.fileName,
+ painter = file.asListingItem().asPainter(),
+ iconTint = file.asListingItem().asTint(),
+ title = file.itemName,
canDeleteFiles = canDeleteFiles,
- subtitle = file.size.toFormattedSize(),
+ subtitle = when (file) {
+ is ExtendedListingItem.File -> file.size.toFormattedSize()
+ is ExtendedListingItem.Folder -> stringResource(
+ resource = FML.string.fml_items_in_folder,
+ file.itemsCount ?: 0
+ )
+ },
+ isSubtitleLoading = when (file) {
+ is ExtendedListingItem.File -> false
+ is ExtendedListingItem.Folder -> file.itemsCount == null
+ },
selectionState = when {
selectionState.selected.contains(filePathWithType) -> ItemUiSelectionState.SELECTED
selectionState.isEnabled -> ItemUiSelectionState.UNSELECTED
else -> ItemUiSelectionState.NONE
},
onClick = {
- when (file.fileType) {
+ when (file.itemType) {
FileType.DIR -> {
onPathChanged.invoke(filePathWithType.fullPath)
}
@@ -77,13 +91,11 @@ fun LazyGridScope.LoadedFilesComposable(
FileType.FILE -> {
onEditFileClick(filePathWithType.fullPath)
}
-
- null -> Unit
}
},
onCheckChange = { onCheckToggle.invoke(filePathWithType) },
onMoreClick = { onFileMoreClick.invoke(filePathWithType) },
- onDelete = { onDelete.invoke(path.resolve(file.fileName)) },
+ onDelete = { onDelete.invoke(path.resolve(file.itemName)) },
orientation = orientation
)
}
diff --git a/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/composable/appbar/FileListAppBar.kt b/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/composable/appbar/FileListAppBar.kt
index 2bb94bd6a4..854467e28a 100644
--- a/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/composable/appbar/FileListAppBar.kt
+++ b/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/composable/appbar/FileListAppBar.kt
@@ -70,8 +70,8 @@ fun FileListAppBar(
.orEmpty()
.map {
PathWithType(
- fileType = it.fileType ?: FileType.FILE,
- fullPath = path.resolve(it.fileName)
+ fileType = it.itemType,
+ fullPath = path.resolve(it.itemName)
)
}
selectionViewModel.select(paths)
diff --git a/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/model/ExtendedListingItem.kt b/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/model/ExtendedListingItem.kt
new file mode 100644
index 0000000000..c9a92778cb
--- /dev/null
+++ b/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/model/ExtendedListingItem.kt
@@ -0,0 +1,46 @@
+package com.flipperdevices.filemanager.listing.impl.model
+
+import com.flipperdevices.bridge.connection.feature.storage.api.model.FileType
+import com.flipperdevices.bridge.connection.feature.storage.api.model.ListingItem
+import okio.Path
+
+sealed interface ExtendedListingItem {
+ /**
+ * Local file-only path
+ * example: file.txt, item.svg
+ */
+ val path: Path
+
+ val itemType: FileType
+
+ val itemName: String
+ get() = path.name
+
+ fun asListingItem() = ListingItem(
+ fileName = itemName,
+ fileType = itemType,
+ size = (this as? File)?.size ?: 0
+ )
+
+ /**
+ * @param path file name path. Not full path
+ * @param size file size in bytes
+ */
+ data class File(
+ override val path: Path,
+ val size: Long
+ ) : ExtendedListingItem {
+ override val itemType: FileType = FileType.FILE
+ }
+
+ /**
+ * @param path file name path. Not full path
+ * @param itemsCount amount of items inside
+ */
+ data class Folder(
+ override val path: Path,
+ val itemsCount: Int? = null,
+ ) : ExtendedListingItem {
+ override val itemType: FileType = FileType.DIR
+ }
+}
diff --git a/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/viewmodel/FilesViewModel.kt b/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/viewmodel/FilesViewModel.kt
index 6e908971a1..d1e3179262 100644
--- a/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/viewmodel/FilesViewModel.kt
+++ b/components/filemngr/listing/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/listing/impl/viewmodel/FilesViewModel.kt
@@ -7,14 +7,17 @@ import com.flipperdevices.bridge.connection.feature.provider.api.get
import com.flipperdevices.bridge.connection.feature.provider.api.getSync
import com.flipperdevices.bridge.connection.feature.storage.api.FStorageFeatureApi
import com.flipperdevices.bridge.connection.feature.storage.api.fm.FListingStorageApi
+import com.flipperdevices.bridge.connection.feature.storage.api.model.FileType
import com.flipperdevices.bridge.connection.feature.storage.api.model.ListingItem
import com.flipperdevices.core.ktx.jre.launchWithLock
import com.flipperdevices.core.ktx.jre.toThrowableFlow
import com.flipperdevices.core.ktx.jre.withLock
import com.flipperdevices.core.log.LogTagProvider
+import com.flipperdevices.core.log.error
import com.flipperdevices.core.preference.pb.FileManagerSort
import com.flipperdevices.core.preference.pb.Settings
import com.flipperdevices.core.ui.lifecycle.DecomposeViewModel
+import com.flipperdevices.filemanager.listing.impl.model.ExtendedListingItem
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -24,12 +27,17 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.flow.updateAndGet
import kotlinx.coroutines.sync.Mutex
import okio.Path
+import okio.Path.Companion.toPath
class FilesViewModel @AssistedInject constructor(
private val featureProvider: FFeatureProvider,
@@ -56,7 +64,7 @@ class FilesViewModel @AssistedInject constructor(
if (settings.show_hidden_files_on_flipper) {
true
} else {
- !it.fileName.startsWith(".")
+ !it.path.name.startsWith(".")
}
}
.sortedByDescending {
@@ -64,7 +72,14 @@ class FilesViewModel @AssistedInject constructor(
is FileManagerSort.Unrecognized,
FileManagerSort.DEFAULT -> null
- FileManagerSort.SIZE -> it.size
+ FileManagerSort.SIZE -> {
+ when (it) {
+ is ExtendedListingItem.File -> it.size
+ // The default size for folder is 0
+ // Here's placed 0 so sort works as on flipper
+ is ExtendedListingItem.Folder -> 0
+ }
+ }
}
}
.toImmutableList()
@@ -74,12 +89,62 @@ class FilesViewModel @AssistedInject constructor(
}
).stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading)
+ private suspend fun updateFiles(
+ items: List,
+ listingApi: FListingStorageApi
+ ) {
+ items
+ .filterIsInstance()
+ .filter { directory -> directory.itemsCount == null }
+ .onEach { directory ->
+ _state.update { state ->
+ val loadedState = (state as? State.Loaded)
+ if (loadedState == null) {
+ error { "#updateFiles state changed during update" }
+ return@update state
+ }
+ val newList = loadedState.files.toMutableList()
+ val i = newList.indexOfFirst { item -> item == directory }
+ if (i == -1) {
+ error { "#updateFiles could not find item in list" }
+ return@update loadedState
+ }
+ val itemsCount = listingApi.ls(path.resolve(directory.path).toString())
+ .getOrNull()
+ .orEmpty()
+ .size
+ val updatedDirectory = directory.copy(itemsCount = itemsCount)
+ newList[i] = updatedDirectory
+ loadedState.copy(files = newList.toImmutableList())
+ }
+ }
+ }
+
+ private fun ListingItem.toExtended(): ExtendedListingItem {
+ return when (fileType) {
+ FileType.DIR -> {
+ ExtendedListingItem.Folder(
+ path = fileName.toPath(),
+ itemsCount = null
+ )
+ }
+
+ null, FileType.FILE -> {
+ ExtendedListingItem.File(
+ path = fileName.toPath(),
+ size = size
+ )
+ }
+ }
+ }
+
private suspend fun listFiles(listingApi: FListingStorageApi) {
listingApi.lsFlow(path.toString())
.toThrowableFlow()
.catch { _state.emit(State.CouldNotListPath) }
+ .map { items -> items.map { item -> item.toExtended() } }
.onEach { files ->
- _state.update { state ->
+ _state.updateAndGet { state ->
when (state) {
is State.Loaded -> {
state.copy(state.files.plus(files).toImmutableList())
@@ -97,7 +162,7 @@ class FilesViewModel @AssistedInject constructor(
val loadedState = _state.value as? State.Loaded ?: return
_state.update {
val newFileList = loadedState.files
- .filter { it.fileName != path.name }
+ .filter { it.path.name != path.name }
.toImmutableList()
loadedState.copy(files = newFileList)
}
@@ -120,8 +185,8 @@ class FilesViewModel @AssistedInject constructor(
(state as? State.Loaded)?.let { loadedState ->
val newItemsNames = items.map(ListingItem::fileName)
val newFiles = loadedState.files
- .filter { item -> !newItemsNames.contains(item.fileName) }
- .plus(items)
+ .filter { item -> !newItemsNames.contains(item.itemName) }
+ .plus(items.map { item -> item.toExtended() })
.toImmutableList()
loadedState.copy(files = newFiles)
} ?: state
@@ -152,6 +217,20 @@ class FilesViewModel @AssistedInject constructor(
.get()
.onEach { featureStatus -> invalidate(featureStatus) }
.launchIn(viewModelScope)
+ combine(
+ flow = featureProvider
+ .get()
+ .filterIsInstance>(),
+ flow2 = state
+ .filterIsInstance()
+ .distinctUntilChangedBy { it.files.size },
+ transform = { feature, state ->
+ updateFiles(
+ items = state.files,
+ listingApi = feature.featureApi.listingApi()
+ )
+ }
+ ).launchIn(viewModelScope)
}
sealed interface State {
@@ -159,7 +238,7 @@ class FilesViewModel @AssistedInject constructor(
data object Unsupported : State
data object CouldNotListPath : State
data class Loaded(
- val files: ImmutableList,
+ val files: ImmutableList,
) : State
}
diff --git a/components/filemngr/search/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/search/impl/composable/FolderCardListLazyComposable.kt b/components/filemngr/search/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/search/impl/composable/FolderCardListLazyComposable.kt
index c84b770d28..116ce9ba74 100644
--- a/components/filemngr/search/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/search/impl/composable/FolderCardListLazyComposable.kt
+++ b/components/filemngr/search/impl/src/commonMain/kotlin/com/flipperdevices/filemanager/search/impl/composable/FolderCardListLazyComposable.kt
@@ -29,6 +29,7 @@ fun LazyListScope.FolderCardListLazyComposable(
subtitle = file.fullPath.parent
?.toString()
?: file.instance.size.toFormattedSize(),
+ isSubtitleLoading = false,
selectionState = ItemUiSelectionState.NONE,
onClick = {
when (file.instance.fileType) {
diff --git a/components/filemngr/ui-components/src/androidMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardGridComposablePreview.kt b/components/filemngr/ui-components/src/androidMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardGridComposablePreview.kt
index a1478b4dcb..7f575e7a80 100644
--- a/components/filemngr/ui-components/src/androidMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardGridComposablePreview.kt
+++ b/components/filemngr/ui-components/src/androidMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardGridComposablePreview.kt
@@ -24,7 +24,8 @@ private fun FolderCardGridComposablePreview() {
selectionState = selectionState,
onClick = {},
onCheckChange = {},
- onMoreClick = {}
+ onMoreClick = {},
+ isSubtitleLoading = false
)
}
ItemUiSelectionState.entries.forEach { selectionState ->
@@ -35,7 +36,20 @@ private fun FolderCardGridComposablePreview() {
selectionState = selectionState,
onClick = {},
onCheckChange = {},
- onMoreClick = {}
+ onMoreClick = {},
+ isSubtitleLoading = false
+ )
+ }
+ ItemUiSelectionState.entries.forEach { selectionState ->
+ FolderCardGridComposable(
+ painter = painterResource(FR.drawable.ic_folder_black),
+ title = "A very very ultra mega super duper log title with some message at the end",
+ subtitle = "A very very ultra mega super duper log title with some message at the end",
+ selectionState = selectionState,
+ onClick = {},
+ onCheckChange = {},
+ onMoreClick = {},
+ isSubtitleLoading = true
)
}
}
diff --git a/components/filemngr/ui-components/src/androidMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardListComposablePreview.kt b/components/filemngr/ui-components/src/androidMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardListComposablePreview.kt
index cbb5d4c555..09ec1ba252 100644
--- a/components/filemngr/ui-components/src/androidMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardListComposablePreview.kt
+++ b/components/filemngr/ui-components/src/androidMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardListComposablePreview.kt
@@ -24,6 +24,7 @@ private fun FolderCardListComposablePreview() {
painter = painterResource(FR.drawable.ic_folder_black),
title = "Short title",
subtitle = "Short SubTitle",
+ isSubtitleLoading = false,
selectionState = selectionState,
canDeleteFiles = true,
onClick = {},
@@ -39,6 +40,21 @@ private fun FolderCardListComposablePreview() {
subtitle = "A very very ultra mega super duper log title with some message at the end",
selectionState = selectionState,
canDeleteFiles = true,
+ isSubtitleLoading = false,
+ onClick = {},
+ onCheckChange = {},
+ onMoreClick = {},
+ onDelete = {}
+ )
+ }
+ ItemUiSelectionState.entries.forEach { selectionState ->
+ SwipeToDismissFolderCardListComposable(
+ painter = rememberVectorPainter(Icons.Filled.Folder),
+ title = "A very very ultra mega super duper log title with some message at the end",
+ subtitle = "A very very ultra mega super duper log title with some message at the end",
+ selectionState = selectionState,
+ canDeleteFiles = true,
+ isSubtitleLoading = true,
onClick = {},
onCheckChange = {},
onMoreClick = {},
diff --git a/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardComposable.kt b/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardComposable.kt
index c56096726d..236ee52f5b 100644
--- a/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardComposable.kt
+++ b/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardComposable.kt
@@ -12,6 +12,7 @@ fun FolderCardComposable(
painter: Painter,
title: String,
subtitle: String,
+ isSubtitleLoading: Boolean,
selectionState: ItemUiSelectionState,
canDeleteFiles: Boolean,
onClick: () -> Unit,
@@ -28,6 +29,7 @@ fun FolderCardComposable(
painter = painter,
title = title,
subtitle = subtitle,
+ isSubtitleLoading = isSubtitleLoading,
selectionState = selectionState,
onClick = onClick,
onCheckChange = onCheckChange,
@@ -43,6 +45,7 @@ fun FolderCardComposable(
painter = painter,
title = title,
subtitle = subtitle,
+ isSubtitleLoading = isSubtitleLoading,
selectionState = selectionState,
onClick = onClick,
onCheckChange = onCheckChange,
diff --git a/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardGridComposable.kt b/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardGridComposable.kt
index 8f068dc2b6..34a3c010f0 100644
--- a/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardGridComposable.kt
+++ b/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardGridComposable.kt
@@ -29,6 +29,7 @@ fun FolderCardGridComposable(
painter: Painter,
title: String,
subtitle: String,
+ isSubtitleLoading: Boolean,
selectionState: ItemUiSelectionState,
onClick: () -> Unit,
onCheckChange: (Boolean) -> Unit,
@@ -62,7 +63,10 @@ fun FolderCardGridComposable(
horizontalAlignment = Alignment.Start
) {
ItemCardTitle(title)
- ItemCardSubtitle(subtitle)
+ ItemCardSubtitle(
+ text = subtitle,
+ isLoading = isSubtitleLoading
+ )
}
}
diff --git a/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardListComposable.kt b/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardListComposable.kt
index 79e47eb3f9..fda29be805 100644
--- a/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardListComposable.kt
+++ b/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/FolderCardListComposable.kt
@@ -37,6 +37,7 @@ fun SwipeToDismissFolderCardListComposable(
painter: Painter,
title: String,
subtitle: String,
+ isSubtitleLoading: Boolean,
selectionState: ItemUiSelectionState,
canDeleteFiles: Boolean,
onClick: () -> Unit,
@@ -63,6 +64,7 @@ fun SwipeToDismissFolderCardListComposable(
onMoreClick = onMoreClick,
onClick = onClick,
iconTint = iconTint,
+ isSubtitleLoading = isSubtitleLoading
)
},
actions = {
@@ -104,6 +106,7 @@ fun FolderCardListComposable(
painter: Painter,
title: String,
subtitle: String,
+ isSubtitleLoading: Boolean,
selectionState: ItemUiSelectionState,
onClick: () -> Unit,
onCheckChange: (Boolean) -> Unit,
@@ -138,7 +141,10 @@ fun FolderCardListComposable(
horizontalAlignment = Alignment.Start
) {
ItemCardTitle(title)
- ItemCardSubtitle(subtitle)
+ ItemCardSubtitle(
+ text = subtitle,
+ isLoading = isSubtitleLoading
+ )
}
}
diff --git a/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/components/ItemCardSubtitle.kt b/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/components/ItemCardSubtitle.kt
index 5853e0c7c9..386d5d3ae1 100644
--- a/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/components/ItemCardSubtitle.kt
+++ b/components/filemngr/ui-components/src/commonMain/kotlin/com/flipperdevices/filemanager/ui/components/itemcard/components/ItemCardSubtitle.kt
@@ -2,12 +2,20 @@ package com.flipperdevices.filemanager.ui.components.itemcard.components
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.flipperdevices.core.ui.ktx.placeholderConnecting
import com.flipperdevices.core.ui.theme.LocalPalletV2
import com.flipperdevices.core.ui.theme.LocalTypography
@Composable
-internal fun ItemCardSubtitle(text: String) {
+internal fun ItemCardSubtitle(
+ text: String,
+ isLoading: Boolean,
+ modifier: Modifier = Modifier
+) {
Text(
+ modifier = modifier
+ .placeholderConnecting(visible = isLoading),
text = text,
style = LocalTypography.current.subtitleM10,
color = LocalPalletV2.current.text.label.secondary