refactor: Remove placeholder buttons for location and contact in QuickActionsRow
This commit is contained in:
@@ -96,6 +96,9 @@ dependencies {
|
|||||||
// uCrop for image cropping
|
// uCrop for image cropping
|
||||||
implementation("com.github.yalantis:ucrop:2.2.8")
|
implementation("com.github.yalantis:ucrop:2.2.8")
|
||||||
|
|
||||||
|
// PhotoEditor for drawing, filters, text on images
|
||||||
|
implementation("com.burhanrashid52:photoeditor:3.0.2")
|
||||||
|
|
||||||
// Blurhash for image placeholders
|
// Blurhash for image placeholders
|
||||||
implementation("com.vanniktech:blurhash:0.1.0")
|
implementation("com.vanniktech:blurhash:0.1.0")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,509 @@
|
|||||||
|
package com.rosetta.messenger.ui.chats.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.*
|
||||||
|
import androidx.compose.foundation.*
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
|
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||||
|
import ja.burhanrashid52.photoeditor.PhotoEditor
|
||||||
|
import ja.burhanrashid52.photoeditor.PhotoEditorView
|
||||||
|
import ja.burhanrashid52.photoeditor.SaveSettings
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
private const val TAG = "ImageEditorScreen"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available editing tools
|
||||||
|
*/
|
||||||
|
enum class EditorTool {
|
||||||
|
NONE,
|
||||||
|
DRAW
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drawing colors
|
||||||
|
*/
|
||||||
|
val drawingColors = listOf(
|
||||||
|
Color.White,
|
||||||
|
Color.Black,
|
||||||
|
Color.Red,
|
||||||
|
Color(0xFFFF9500), // Orange
|
||||||
|
Color.Yellow,
|
||||||
|
Color(0xFF34C759), // Green
|
||||||
|
Color(0xFF007AFF), // Blue
|
||||||
|
Color(0xFF5856D6), // Purple
|
||||||
|
Color(0xFFFF2D55), // Pink
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Telegram-style image editor screen
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ImageEditorScreen(
|
||||||
|
imageUri: Uri,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onSave: (Uri) -> Unit,
|
||||||
|
isDarkTheme: Boolean = true
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
// Editor state
|
||||||
|
var currentTool by remember { mutableStateOf(EditorTool.NONE) }
|
||||||
|
var selectedColor by remember { mutableStateOf(Color.White) }
|
||||||
|
var brushSize by remember { mutableStateOf(10f) }
|
||||||
|
var showColorPicker by remember { mutableStateOf(false) }
|
||||||
|
var showBrushSizeSlider by remember { mutableStateOf(false) }
|
||||||
|
var isSaving by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// PhotoEditor reference
|
||||||
|
var photoEditor by remember { mutableStateOf<PhotoEditor?>(null) }
|
||||||
|
|
||||||
|
BackHandler {
|
||||||
|
onDismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Black)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
// Top toolbar
|
||||||
|
TopAppBar(
|
||||||
|
title = { },
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onDismiss) {
|
||||||
|
Icon(
|
||||||
|
TablerIcons.X,
|
||||||
|
contentDescription = "Close",
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
// Undo
|
||||||
|
IconButton(
|
||||||
|
onClick = { photoEditor?.undo() }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
TablerIcons.ArrowBackUp,
|
||||||
|
contentDescription = "Undo",
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redo
|
||||||
|
IconButton(
|
||||||
|
onClick = { photoEditor?.redo() }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
TablerIcons.ArrowForwardUp,
|
||||||
|
contentDescription = "Redo",
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done/Save button
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
isSaving = true
|
||||||
|
saveEditedImage(context, photoEditor) { savedUri ->
|
||||||
|
isSaving = false
|
||||||
|
if (savedUri != null) {
|
||||||
|
onSave(savedUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = !isSaving
|
||||||
|
) {
|
||||||
|
if (isSaving) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
|
color = PrimaryBlue,
|
||||||
|
strokeWidth = 2.dp
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
"Done",
|
||||||
|
color = PrimaryBlue,
|
||||||
|
fontWeight = FontWeight.SemiBold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
|
containerColor = Color.Transparent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Photo Editor View
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
AndroidView(
|
||||||
|
factory = { ctx ->
|
||||||
|
PhotoEditorView(ctx).apply {
|
||||||
|
// Load image - fullscreen
|
||||||
|
source.setImageURI(imageUri)
|
||||||
|
source.scaleType = ImageView.ScaleType.CENTER_CROP
|
||||||
|
|
||||||
|
// Build PhotoEditor
|
||||||
|
photoEditor = PhotoEditor.Builder(ctx, this)
|
||||||
|
.setPinchTextScalable(true)
|
||||||
|
.setClipSourceImage(true)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color picker bar (when drawing)
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = currentTool == EditorTool.DRAW && showColorPicker,
|
||||||
|
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
|
||||||
|
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut()
|
||||||
|
) {
|
||||||
|
ColorPickerBar(
|
||||||
|
selectedColor = selectedColor,
|
||||||
|
onColorSelected = { color ->
|
||||||
|
selectedColor = color
|
||||||
|
photoEditor?.brushColor = color.toArgb()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Brush size slider
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = currentTool == EditorTool.DRAW && showBrushSizeSlider,
|
||||||
|
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
|
||||||
|
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut()
|
||||||
|
) {
|
||||||
|
BrushSizeBar(
|
||||||
|
brushSize = brushSize,
|
||||||
|
onBrushSizeChanged = { size ->
|
||||||
|
brushSize = size
|
||||||
|
photoEditor?.brushSize = size
|
||||||
|
},
|
||||||
|
selectedColor = selectedColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom toolbar with tools
|
||||||
|
BottomToolbar(
|
||||||
|
currentTool = currentTool,
|
||||||
|
onToolSelected = { tool ->
|
||||||
|
when (tool) {
|
||||||
|
EditorTool.DRAW -> {
|
||||||
|
if (currentTool == EditorTool.DRAW) {
|
||||||
|
showColorPicker = !showColorPicker
|
||||||
|
showBrushSizeSlider = false
|
||||||
|
} else {
|
||||||
|
currentTool = tool
|
||||||
|
photoEditor?.setBrushDrawingMode(true)
|
||||||
|
photoEditor?.brushColor = selectedColor.toArgb()
|
||||||
|
photoEditor?.brushSize = brushSize
|
||||||
|
showColorPicker = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
currentTool = EditorTool.NONE
|
||||||
|
showColorPicker = false
|
||||||
|
showBrushSizeSlider = false
|
||||||
|
photoEditor?.setBrushDrawingMode(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onBrushSizeClick = {
|
||||||
|
showBrushSizeSlider = !showBrushSizeSlider
|
||||||
|
showColorPicker = false
|
||||||
|
},
|
||||||
|
onEraserClick = {
|
||||||
|
photoEditor?.brushEraser()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BottomToolbar(
|
||||||
|
currentTool: EditorTool,
|
||||||
|
onToolSelected: (EditorTool) -> Unit,
|
||||||
|
onBrushSizeClick: () -> Unit,
|
||||||
|
onEraserClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
color = Color(0xFF1C1C1E),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||||
|
.navigationBarsPadding(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// Draw tool
|
||||||
|
ToolButton(
|
||||||
|
icon = TablerIcons.Pencil,
|
||||||
|
label = "Draw",
|
||||||
|
isSelected = currentTool == EditorTool.DRAW,
|
||||||
|
onClick = { onToolSelected(EditorTool.DRAW) }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Eraser (when drawing)
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = currentTool == EditorTool.DRAW,
|
||||||
|
enter = fadeIn() + scaleIn(),
|
||||||
|
exit = fadeOut() + scaleOut()
|
||||||
|
) {
|
||||||
|
ToolButton(
|
||||||
|
icon = TablerIcons.Eraser,
|
||||||
|
label = "Eraser",
|
||||||
|
isSelected = false,
|
||||||
|
onClick = onEraserClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Brush size (when drawing)
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = currentTool == EditorTool.DRAW,
|
||||||
|
enter = fadeIn() + scaleIn(),
|
||||||
|
exit = fadeOut() + scaleOut()
|
||||||
|
) {
|
||||||
|
ToolButton(
|
||||||
|
icon = TablerIcons.Circle,
|
||||||
|
label = "Size",
|
||||||
|
isSelected = false,
|
||||||
|
onClick = onBrushSizeClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ToolButton(
|
||||||
|
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||||
|
label: String,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = label,
|
||||||
|
tint = if (isSelected) PrimaryBlue else Color.White,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
color = if (isSelected) PrimaryBlue else Color.White.copy(alpha = 0.7f),
|
||||||
|
fontSize = 10.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColorPickerBar(
|
||||||
|
selectedColor: Color,
|
||||||
|
onColorSelected: (Color) -> Unit
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
color = Color(0xFF2C2C2E),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
contentPadding = PaddingValues(horizontal = 8.dp)
|
||||||
|
) {
|
||||||
|
items(drawingColors) { color ->
|
||||||
|
ColorButton(
|
||||||
|
color = color,
|
||||||
|
isSelected = color == selectedColor,
|
||||||
|
onClick = { onColorSelected(color) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColorButton(
|
||||||
|
color: Color,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(color)
|
||||||
|
.then(
|
||||||
|
if (isSelected) {
|
||||||
|
Modifier.border(3.dp, Color.White, CircleShape)
|
||||||
|
} else {
|
||||||
|
Modifier.border(1.dp, Color.White.copy(alpha = 0.3f), CircleShape)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.clickable(onClick = onClick),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
if (isSelected) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Check,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = if (color == Color.White) Color.Black else Color.White,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BrushSizeBar(
|
||||||
|
brushSize: Float,
|
||||||
|
onBrushSizeChanged: (Float) -> Unit,
|
||||||
|
selectedColor: Color
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
color = Color(0xFF2C2C2E),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 24.dp, vertical = 16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// Min indicator
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(8.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(selectedColor)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Slider
|
||||||
|
Slider(
|
||||||
|
value = brushSize,
|
||||||
|
onValueChange = onBrushSizeChanged,
|
||||||
|
valueRange = 5f..50f,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
colors = SliderDefaults.colors(
|
||||||
|
thumbColor = selectedColor,
|
||||||
|
activeTrackColor = selectedColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Max indicator
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(selectedColor)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save edited image and return the URI
|
||||||
|
*/
|
||||||
|
private suspend fun saveEditedImage(
|
||||||
|
context: Context,
|
||||||
|
photoEditor: PhotoEditor?,
|
||||||
|
onResult: (Uri?) -> Unit
|
||||||
|
) {
|
||||||
|
if (photoEditor == null) {
|
||||||
|
onResult(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val file = File(context.cacheDir, "edited_${System.currentTimeMillis()}.png")
|
||||||
|
|
||||||
|
val saveSettings = SaveSettings.Builder()
|
||||||
|
.setClearViewsEnabled(false)
|
||||||
|
.setTransparencyEnabled(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
photoEditor.saveAsFile(
|
||||||
|
file.absolutePath,
|
||||||
|
saveSettings,
|
||||||
|
object : PhotoEditor.OnSaveListener {
|
||||||
|
override fun onSuccess(imagePath: String) {
|
||||||
|
Log.d(TAG, "Image saved to: $imagePath")
|
||||||
|
onResult(Uri.fromFile(File(imagePath)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(exception: Exception) {
|
||||||
|
Log.e(TAG, "Failed to save image", exception)
|
||||||
|
onResult(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error saving image", e)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
onResult(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,6 +85,9 @@ fun MediaPickerBottomSheet(
|
|||||||
// Selected items
|
// Selected items
|
||||||
var selectedItems by remember { mutableStateOf<Set<Long>>(emptySet()) }
|
var selectedItems by remember { mutableStateOf<Set<Long>>(emptySet()) }
|
||||||
|
|
||||||
|
// Editor state - when user taps on a photo, open editor
|
||||||
|
var editingItem by remember { mutableStateOf<MediaItem?>(null) }
|
||||||
|
|
||||||
// Permission launcher
|
// Permission launcher
|
||||||
val permissionLauncher = rememberLauncherForActivityResult(
|
val permissionLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.RequestMultiplePermissions()
|
contract = ActivityResultContracts.RequestMultiplePermissions()
|
||||||
@@ -253,6 +256,23 @@ fun MediaPickerBottomSheet(
|
|||||||
mediaItems = mediaItems,
|
mediaItems = mediaItems,
|
||||||
selectedItems = selectedItems,
|
selectedItems = selectedItems,
|
||||||
onItemClick = { item ->
|
onItemClick = { item ->
|
||||||
|
// Single tap - open editor for images, or select for videos
|
||||||
|
if (!item.isVideo) {
|
||||||
|
// Open image editor
|
||||||
|
editingItem = item
|
||||||
|
} else {
|
||||||
|
// For videos - just toggle selection
|
||||||
|
selectedItems = if (item.id in selectedItems) {
|
||||||
|
selectedItems - item.id
|
||||||
|
} else if (selectedItems.size < maxSelection) {
|
||||||
|
selectedItems + item.id
|
||||||
|
} else {
|
||||||
|
selectedItems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onItemLongClick = { item ->
|
||||||
|
// Long press - toggle selection (multi-select mode)
|
||||||
selectedItems = if (item.id in selectedItems) {
|
selectedItems = if (item.id in selectedItems) {
|
||||||
selectedItems - item.id
|
selectedItems - item.id
|
||||||
} else if (selectedItems.size < maxSelection) {
|
} else if (selectedItems.size < maxSelection) {
|
||||||
@@ -261,9 +281,6 @@ fun MediaPickerBottomSheet(
|
|||||||
selectedItems
|
selectedItems
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onItemLongClick = { item ->
|
|
||||||
// TODO: Preview image
|
|
||||||
},
|
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
@@ -274,6 +291,27 @@ fun MediaPickerBottomSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Image Editor overlay
|
||||||
|
editingItem?.let { item ->
|
||||||
|
ImageEditorScreen(
|
||||||
|
imageUri = item.uri,
|
||||||
|
onDismiss = { editingItem = null },
|
||||||
|
onSave = { editedUri ->
|
||||||
|
editingItem = null
|
||||||
|
// Create a new MediaItem with the edited URI
|
||||||
|
val editedItem = MediaItem(
|
||||||
|
id = System.currentTimeMillis(),
|
||||||
|
uri = editedUri,
|
||||||
|
mimeType = "image/png",
|
||||||
|
dateModified = System.currentTimeMillis()
|
||||||
|
)
|
||||||
|
onMediaSelected(listOf(editedItem))
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
isDarkTheme = isDarkTheme
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -363,24 +401,6 @@ private fun QuickActionsRow(
|
|||||||
iconColor = iconColor,
|
iconColor = iconColor,
|
||||||
onClick = onFileClick
|
onClick = onFileClick
|
||||||
)
|
)
|
||||||
|
|
||||||
// Location button (placeholder)
|
|
||||||
QuickActionButton(
|
|
||||||
icon = TablerIcons.MapPin,
|
|
||||||
label = "Location",
|
|
||||||
backgroundColor = buttonColor,
|
|
||||||
iconColor = iconColor,
|
|
||||||
onClick = { /* TODO */ }
|
|
||||||
)
|
|
||||||
|
|
||||||
// Contact button (placeholder)
|
|
||||||
QuickActionButton(
|
|
||||||
icon = TablerIcons.User,
|
|
||||||
label = "Contact",
|
|
||||||
backgroundColor = buttonColor,
|
|
||||||
iconColor = iconColor,
|
|
||||||
onClick = { /* TODO */ }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user