mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 12:53:55 +01:00
create custom selector main screen with UI events and folder data class
This commit is contained in:
parent
74cf035663
commit
9f7ae759a2
4 changed files with 270 additions and 0 deletions
|
|
@ -0,0 +1,6 @@
|
|||
package fr.free.nrw.commons.customselector.ui.screens
|
||||
|
||||
interface CustomSelectorEvent {
|
||||
data class OnFolderClick(val bucketId: Long): CustomSelectorEvent
|
||||
data class OnImageSelect(val imageId: Long): CustomSelectorEvent
|
||||
}
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
package fr.free.nrw.commons.customselector.ui.screens
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.material3.adaptive.layout.AnimatedPane
|
||||
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
|
||||
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
|
||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.window.core.layout.WindowWidthSizeClass
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.customselector.ui.components.CustomSelectorBottomBar
|
||||
import fr.free.nrw.commons.customselector.ui.components.CustomSelectorTopBar
|
||||
import fr.free.nrw.commons.customselector.ui.components.PartialStorageAccessDialog
|
||||
import fr.free.nrw.commons.ui.theme.CommonsTheme
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun CustomSelectorScreen(
|
||||
uiState: CustomSelectorState,
|
||||
onEvent: (CustomSelectorEvent)-> Unit,
|
||||
selectedImageIds: List<Long>
|
||||
) {
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val navigator = rememberListDetailPaneScaffoldNavigator<Folder>()
|
||||
|
||||
BackHandler(navigator.canNavigateBack()) {
|
||||
navigator.navigateBack()
|
||||
}
|
||||
LaunchedEffect(key1 = navigator.currentDestination, key2 = navigator.scaffoldValue) {
|
||||
println("Current Dest:- ${navigator.currentDestination} | Scaffold Value:- ${navigator.scaffoldValue}")
|
||||
}
|
||||
|
||||
ListDetailPaneScaffold(
|
||||
directive = navigator.scaffoldDirective.copy(horizontalPartitionSpacerSize = 0.dp),
|
||||
value = navigator.scaffoldValue,
|
||||
listPane = {
|
||||
AnimatedPane {
|
||||
FoldersPane(uiState = uiState,
|
||||
onFolderClick = {
|
||||
onEvent(CustomSelectorEvent.OnFolderClick(it.bucketId))
|
||||
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, it)
|
||||
},
|
||||
adaptiveInfo = adaptiveInfo
|
||||
)
|
||||
}
|
||||
},
|
||||
detailPane = {
|
||||
AnimatedPane(modifier = Modifier) {
|
||||
navigator.currentDestination?.content?.let { folder->
|
||||
ImagesPane(
|
||||
selectedFolder = folder,
|
||||
selectedImages = selectedImageIds,
|
||||
imageList = uiState.filteredImages,
|
||||
onNavigateBack = { navigator.navigateBack() },
|
||||
onToggleImageSelection = { onEvent(CustomSelectorEvent.OnImageSelect(it)) },
|
||||
adaptiveInfo = adaptiveInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FoldersPane(
|
||||
uiState: CustomSelectorState,
|
||||
onFolderClick: (Folder)-> Unit,
|
||||
adaptiveInfo: WindowAdaptiveInfo
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
Surface(tonalElevation = 1.dp) {
|
||||
CustomSelectorTopBar(
|
||||
primaryText = stringResource(R.string.custom_selector_title),
|
||||
onNavigateBack = { /*TODO*/ },
|
||||
showAlertIcon = true
|
||||
)
|
||||
}
|
||||
},
|
||||
bottomBar = {
|
||||
Surface(tonalElevation = 1.dp) {
|
||||
CustomSelectorBottomBar(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
) { innerPadding->
|
||||
Surface(tonalElevation = 0.dp) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
if(adaptiveInfo.windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT) {
|
||||
PartialStorageAccessDialog(
|
||||
isVisible = true,
|
||||
onManage = { /*TODO*/ },
|
||||
modifier = Modifier.padding(8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
if(uiState.isLoading) {
|
||||
CircularProgressIndicator()
|
||||
} else {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(164.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
modifier = Modifier.fillMaxSize(1f)
|
||||
) {
|
||||
items(uiState.folders, key = { it.bucketId }) {
|
||||
FolderItem(
|
||||
previewPainter = rememberAsyncImagePainter(model = it.preview),
|
||||
folderName = it.bucketName,
|
||||
itemsCount = it.itemsCount,
|
||||
onClick = { onFolderClick(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FolderItem(
|
||||
previewPainter: Painter,
|
||||
folderName: String,
|
||||
itemsCount: Int,
|
||||
onClick: ()-> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.aspectRatio(1f)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable { onClick() }
|
||||
) {
|
||||
Image(
|
||||
painter = previewPainter,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.aspectRatio(1f),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Text(
|
||||
text = "$itemsCount",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.widthIn(min = 32.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.clip(RoundedCornerShape(bottomStart = 12.dp))
|
||||
.background(color = MaterialTheme.colorScheme.secondaryContainer)
|
||||
.padding(4.dp)
|
||||
)
|
||||
Surface(
|
||||
modifier = Modifier.align(Alignment.BottomStart),
|
||||
color = MaterialTheme.colorScheme.secondaryContainer
|
||||
) {
|
||||
Text(
|
||||
text = folderName,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun FolderItemPreview() {
|
||||
CommonsTheme {
|
||||
Surface {
|
||||
FolderItem(
|
||||
previewPainter = painterResource(R.drawable.placeholder_image),
|
||||
folderName = "Folder Name",
|
||||
itemsCount = 12,
|
||||
onClick = { },
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.size(164.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun CustomSelectorScreenPreview() {
|
||||
CommonsTheme {
|
||||
CustomSelectorScreen(
|
||||
uiState = CustomSelectorState(),
|
||||
onEvent = { },
|
||||
selectedImageIds = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package fr.free.nrw.commons.customselector.ui.screens
|
||||
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
|
||||
data class CustomSelectorState(
|
||||
val isLoading: Boolean = false,
|
||||
val folders: List<Folder> = emptyList(),
|
||||
val filteredImages: List<Image> = emptyList()
|
||||
)
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package fr.free.nrw.commons.customselector.ui.screens
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class Folder(
|
||||
val bucketId: Long,
|
||||
val bucketName: String,
|
||||
val preview: Uri,
|
||||
val itemsCount: Int
|
||||
): Parcelable
|
||||
Loading…
Add table
Add a link
Reference in a new issue