Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
2024-09-18 04:46:55 +08:00
4 changed files with 37 additions and 27 deletions

View File

@@ -46,6 +46,7 @@ import java.util.UUID
@Composable @Composable
fun <T : Any> DraggableGrid( fun <T : Any> DraggableGrid(
items: List<T>, items: List<T>,
getItemId: (T) -> String,
onMove: (Int, Int) -> Unit, onMove: (Int, Int) -> Unit,
onDragModeStart: () -> Unit, // New parameter for drag start onDragModeStart: () -> Unit, // New parameter for drag start
onDragModeEnd: () -> Unit, // New parameter for drag end, onDragModeEnd: () -> Unit, // New parameter for drag end,
@@ -66,7 +67,9 @@ fun <T : Any> DraggableGrid(
horizontalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
) { ) {
itemsIndexed(items, key = { _, item -> "${item}-${UUID.randomUUID()}" }) { index, item -> itemsIndexed(items, key = { _, item ->
getItemId(item)
}) { index, item ->
DraggableItem(dragDropState, index) { isDragging -> DraggableItem(dragDropState, index) { isDragging ->
content(item, isDragging) content(item, isDragging)
} }

View File

@@ -5,7 +5,6 @@ import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -13,7 +12,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
@@ -29,7 +27,6 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.LinearProgressIndicator import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -43,7 +40,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.PathEffect
@@ -226,7 +222,6 @@ fun NewPostTextField(hint: String, value: String, onValueChange: (String) -> Uni
} }
@Composable @Composable
fun AddImageGrid() { fun AddImageGrid() {
val navController = LocalNavController.current val navController = LocalNavController.current
@@ -237,7 +232,12 @@ fun AddImageGrid() {
contract = ActivityResultContracts.GetMultipleContents() contract = ActivityResultContracts.GetMultipleContents()
) { uris -> ) { uris ->
if (uris.isNotEmpty()) { if (uris.isNotEmpty()) {
model.imageUriList += uris.map { it.toString() } model.imageList += uris.map {
ImageItem(
uri = it.toString(),
id = java.util.UUID.randomUUID().toString()
)
}
} }
} }
@@ -245,7 +245,10 @@ fun AddImageGrid() {
contract = ActivityResultContracts.TakePicture() contract = ActivityResultContracts.TakePicture()
) { success -> ) { success ->
if (success) { if (success) {
model.imageUriList += model.currentPhotoUri.toString() model.imageList += ImageItem(
uri = model.currentPhotoUri.toString(),
id = java.util.UUID.randomUUID().toString()
)
} }
} }
@@ -254,9 +257,9 @@ fun AddImageGrid() {
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f) pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
) )
DraggableGrid( DraggableGrid(
items = NewPostViewModel.imageUriList, items = NewPostViewModel.imageList,
onMove = { from, to -> onMove = { from, to ->
NewPostViewModel.imageUriList = NewPostViewModel.imageUriList.toMutableList().apply { NewPostViewModel.imageList = NewPostViewModel.imageList.toMutableList().apply {
add(to, removeAt(from)) add(to, removeAt(from))
} }
}, },
@@ -268,14 +271,14 @@ fun AddImageGrid() {
additionalItems = listOf( additionalItems = listOf(
), ),
getItemId = { it.id }
) { item, isDrag -> ) { item, isDrag ->
Box( Box(
modifier = Modifier modifier = Modifier
) { ) {
CustomAsyncImage( CustomAsyncImage(
LocalContext.current, LocalContext.current,
item, item.uri,
contentDescription = "Image", contentDescription = "Image",
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@@ -24,9 +24,6 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -42,7 +39,7 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
@Composable @Composable
fun NewPostImageGridScreen() { fun NewPostImageGridScreen() {
val model = NewPostViewModel val model = NewPostViewModel
val imageList = model.imageUriList val imageList = model.imageList
val pagerState = rememberPagerState(pageCount = { imageList.size }) val pagerState = rememberPagerState(pageCount = { imageList.size })
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
val paddingValues = WindowInsets.systemBars.asPaddingValues() val paddingValues = WindowInsets.systemBars.asPaddingValues()

View File

@@ -19,14 +19,19 @@ import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
import java.util.UUID
data class ImageItem(
val uri: String,
val id: String
)
object NewPostViewModel : ViewModel() { object NewPostViewModel : ViewModel() {
var momentService: MomentService = MomentServiceImpl() var momentService: MomentService = MomentServiceImpl()
var textContent by mutableStateOf("") var textContent by mutableStateOf("")
var searchPlaceAddressResult by mutableStateOf<SearchPlaceAddressResult?>(null) var searchPlaceAddressResult by mutableStateOf<SearchPlaceAddressResult?>(null)
var modificationList by mutableStateOf<List<Modification>>(listOf()) var modificationList by mutableStateOf<List<Modification>>(listOf())
var imageUriList by mutableStateOf(listOf<String>()) var imageList by mutableStateOf(listOf<ImageItem>())
var relPostId by mutableStateOf<Int?>(null) var relPostId by mutableStateOf<Int?>(null)
var relMoment by mutableStateOf<MomentEntity?>(null) var relMoment by mutableStateOf<MomentEntity?>(null)
var currentPhotoUri: Uri? = null var currentPhotoUri: Uri? = null
@@ -34,14 +39,16 @@ object NewPostViewModel : ViewModel() {
textContent = "" textContent = ""
searchPlaceAddressResult = null searchPlaceAddressResult = null
modificationList = listOf() modificationList = listOf()
imageUriList = listOf() imageList = listOf()
relPostId = null relPostId = null
} }
fun asNewPostWithImageUris(imageUris: List<String>) { fun asNewPostWithImageUris(imageUris: List<String>) {
textContent = "" textContent = ""
searchPlaceAddressResult = null searchPlaceAddressResult = null
modificationList = listOf() modificationList = listOf()
imageUriList = imageUris imageList = imageUris.map {
ImageItem(it, UUID.randomUUID().toString())
}
relPostId = null relPostId = null
} }
@@ -59,7 +66,7 @@ object NewPostViewModel : ViewModel() {
} }
fun validateMoment(): String? { fun validateMoment(): String? {
if (imageUriList.isEmpty()) { if (imageList.isEmpty()) {
return "Please select at least one image" return "Please select at least one image"
} }
return null return null
@@ -67,21 +74,21 @@ object NewPostViewModel : ViewModel() {
suspend fun createMoment(context: Context, onUploadProgress: (Float) -> Unit) { suspend fun createMoment(context: Context, onUploadProgress: (Float) -> Unit) {
val uploadImageList = emptyList<UploadImage>().toMutableList() val uploadImageList = emptyList<UploadImage>().toMutableList()
var index = 0 var index = 0
for (uri in imageUriList) { for (item in imageList) {
val cursor = context.contentResolver.query(Uri.parse(uri), null, null, null, null) val cursor = context.contentResolver.query(Uri.parse(item.uri), null, null, null, null)
cursor?.use { cursor?.use {
if (it.moveToFirst()) { if (it.moveToFirst()) {
val displayName = it.getString(it.getColumnIndex("_display_name")) val displayName = it.getString(it.getColumnIndex("_display_name"))
val extension = displayName.substringAfterLast(".") val extension = displayName.substringAfterLast(".")
Log.d("NewPost", "File name: $displayName, extension: $extension") Log.d("NewPost", "File name: $displayName, extension: $extension")
// read as file // read as file
val file = uriToFile(context, Uri.parse(uri)) val file = uriToFile(context, Uri.parse(item.uri))
Log.d("NewPost", "File size: ${file.length()}") Log.d("NewPost", "File size: ${file.length()}")
uploadImageList += UploadImage(file, displayName, uri, extension) uploadImageList += UploadImage(file, displayName, item.uri, extension)
} }
} }
// 在上传过程中调用 onUploadProgress 更新进度 // 在上传过程中调用 onUploadProgress 更新进度
onUploadProgress(((index / imageUriList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50% onUploadProgress(((index / imageList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50%
index += 1 index += 1
} }
momentService.createMoment(textContent, 1, uploadImageList, relPostId) momentService.createMoment(textContent, 1, uploadImageList, relPostId)
@@ -97,7 +104,7 @@ object NewPostViewModel : ViewModel() {
} }
} }
fun deleteImage(index: Int) { fun deleteImage(index: Int) {
imageUriList = imageUriList.toMutableList().apply { imageList = imageList.toMutableList().apply {
removeAt(index) removeAt(index)
} }
} }