add logic to mark or unmark images as not for upload

This commit is contained in:
Rohit Verma 2025-01-03 23:14:04 +05:30
parent e611cbc86f
commit d1fdab4d8b
3 changed files with 119 additions and 7 deletions

View file

@ -1,8 +1,14 @@
package fr.free.nrw.commons.customselector.ui.screens
interface CustomSelectorEvent {
import fr.free.nrw.commons.customselector.ui.states.ImageUiState
import kotlinx.coroutines.CoroutineScope
sealed interface CustomSelectorEvent {
data class OnFolderClick(val bucketId: Long): CustomSelectorEvent
data class OnImageSelection(val imageId: Long): CustomSelectorEvent
data class OnDragImageSelection(val imageIds: Set<Long>): CustomSelectorEvent
data object OnUnselectAll: CustomSelectorEvent
data class OnUpdateImageStatus(val scope: CoroutineScope, val image: ImageUiState) : CustomSelectorEvent
data object MarkAsNotForUpload: CustomSelectorEvent
data object UnmarkAsNotForUpload: CustomSelectorEvent
}

View file

@ -1,6 +1,5 @@
package fr.free.nrw.commons.customselector.ui.screens
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import fr.free.nrw.commons.customselector.domain.ImageRepository
@ -83,6 +82,75 @@ class CustomSelectorViewModel @Inject constructor(
CustomSelectorEvent.OnUnselectAll-> {
_uiState.update { it.copy(selectedImageIds = emptySet()) }
}
is CustomSelectorEvent.OnUpdateImageStatus -> {
e.scope.launch { updateNotForUploadStatus(e.image) }
}
is CustomSelectorEvent.MarkAsNotForUpload -> {
viewModelScope.launch {
val selectedImageIds = _uiState.value.selectedImageIds
val selectedImages = allImages.filter { image ->
selectedImageIds.contains(image.id)
}
selectedImages.forEach { image ->
cacheSHA1[image.id]?.let { sha ->
if(!imageRepository.isNotForUpload(sha)) {
imageRepository.markAsNotForUpload(sha)
updateImageStatus(true, image.id)
_uiState.update { it.copy(selectedImageIds = emptySet()) }
}
}
}
}
}
CustomSelectorEvent.UnmarkAsNotForUpload -> {
viewModelScope.launch {
val selectedImageIds = _uiState.value.selectedImageIds
val selectedImages = allImages.filter { image ->
selectedImageIds.contains(image.id)
}
selectedImages.forEach { image ->
cacheSHA1[image.id]?.let { sha ->
if(imageRepository.isNotForUpload(sha)) {
imageRepository.unmarkAsNotForUpload(sha)
updateImageStatus(false, image.id)
_uiState.update { it.copy(selectedImageIds = emptySet()) }
}
}
}
}
}
}
}
private fun updateImageStatus(isNotForUpload: Boolean, imageId: Long) {
_uiState.update { state ->
val updatedImages = state.filteredImages.map {
if (it.id == imageId) {
it.copy(isNotForUpload = isNotForUpload)
} else {
it
}
}
val updateMap = state.imagesNotForUpload.toMutableMap()
updateMap[imageId] = isNotForUpload
state.copy(filteredImages = updatedImages, imagesNotForUpload = updateMap)
}
}
private suspend fun updateNotForUploadStatus(image: ImageUiState) {
val imageSHA = cacheSHA1.getOrPut(image.id) {
imageUseCase.getImageSHA1(image.uri).also { sha -> cacheSHA1[image.id] = sha }
}
val isNotForUpload = imageRepository.isNotForUpload(imageSHA)
updateImageStatus(isNotForUpload, image.id)
}
}

View file

@ -85,6 +85,9 @@ fun ImagesPane(
.windowWidthSizeClass == WindowWidthSizeClass.COMPACT
}
}
val isSelectedImageNotForUpload by remember(uiState.selectedImageIds) {
derivedStateOf { uiState.selectedImageIds.any { uiState.imagesNotForUpload[it] == true } }
}
LaunchedEffect(autoScrollSpeed) {
if (autoScrollSpeed != 0f) {
@ -117,8 +120,16 @@ fun ImagesPane(
Surface(tonalElevation = 1.dp) {
CustomSelectorBottomBar(
onPrimaryAction = { /*TODO("Implement action to upload selected images")*/ },
onSecondaryAction = { /*TODO("Implement action to mark/unmark as not for upload")*/ },
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
onSecondaryAction = {
if(isSelectedImageNotForUpload) {
onEvent(CustomSelectorEvent.UnmarkAsNotForUpload)
} else {
onEvent(CustomSelectorEvent.MarkAsNotForUpload)
}
},
isAnyImageNotForUpload = isSelectedImageNotForUpload,
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.navigationBarsPadding()
)
}
@ -161,6 +172,10 @@ fun ImagesPane(
imagePainter = rememberAsyncImagePainter(model = image.uri),
isSelected = isSelected,
inSelectionMode = uiState.inSelectionMode,
isNotForUpload = image.isNotForUpload,
onImageStatusChange = { scope ->
onEvent(CustomSelectorEvent.OnUpdateImageStatus(scope, image))
},
modifier = Modifier.combinedClickable(
onClick = {
if (uiState.inSelectionMode) {
@ -184,9 +199,16 @@ fun ImagesPane(
fun ImageItem(
imagePainter: Painter,
isSelected: Boolean,
onImageStatusChange: (scope: CoroutineScope) -> Unit,
modifier: Modifier = Modifier,
inSelectionMode: Boolean = false
inSelectionMode: Boolean = false,
isNotForUpload: Boolean = false
) {
// This side-effect updates the image status, like:- isNotForUpload, for visible image only
LaunchedEffect(Unit) {
onImageStatusChange(this)
}
Box(modifier = modifier.clip(RoundedCornerShape(12.dp))) {
Image(
painter = imagePainter,
@ -217,11 +239,25 @@ fun ImageItem(
modifier = Modifier
.size(24.dp)
.clip(RoundedCornerShape(bottomEnd = 12.dp))
.background(color = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.5f))
.padding(2.dp)
.background(
color = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.5f)
).padding(2.dp)
)
}
}
if(isNotForUpload) {
Icon(
painter = painterResource(id = R.drawable.not_for_upload),
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier
.align(Alignment.TopEnd)
.clip(RoundedCornerShape(bottomStart = 12.dp))
.background(color = MaterialTheme.colorScheme.errorContainer)
.padding(4.dp)
)
}
}
}
@ -234,6 +270,8 @@ private fun ImageItemPreview() {
imagePainter = painterResource(id = R.drawable.image_placeholder_96),
isSelected = false,
inSelectionMode = true,
isNotForUpload = true,
onImageStatusChange = { },
modifier = Modifier
.padding(16.dp)
.size(116.dp)