fix: update swipe direction for reply functionality in MessageBubble

This commit is contained in:
k1ngsterr1
2026-02-03 22:56:33 +05:00
parent a7268bb986
commit 44a816dedb
3 changed files with 114 additions and 15 deletions

View File

@@ -321,8 +321,8 @@ fun MessageBubble(
detectHorizontalDragGestures(
onDragStart = { },
onDragEnd = {
// Если свайпнули достаточно вправо - reply
if (swipeOffset >= swipeThreshold) {
// Если свайпнули достаточно влево - reply
if (swipeOffset <= -swipeThreshold) {
onSwipeToReply()
}
swipeOffset = 0f
@@ -331,20 +331,20 @@ fun MessageBubble(
swipeOffset = 0f
},
onHorizontalDrag = { change, dragAmount ->
// Только свайп вправо (положительное значение)
if (dragAmount > 0 || swipeOffset > 0) {
// Только свайп влево (отрицательное значение)
if (dragAmount < 0 || swipeOffset < 0) {
change.consume()
val newOffset = swipeOffset + dragAmount
swipeOffset = newOffset.coerceIn(0f, maxSwipe)
swipeOffset = newOffset.coerceIn(-maxSwipe, 0f)
}
}
)
}
) {
// 🔥 Reply icon - слева, появляется при свайпе вправо
// 🔥 Reply icon - справа, появляется при свайпе влево
Box(
modifier =
Modifier.align(Alignment.CenterStart).padding(start = 16.dp).graphicsLayer {
Modifier.align(Alignment.CenterEnd).padding(end = 16.dp).graphicsLayer {
alpha = swipeProgress
scaleX = swipeProgress
scaleY = swipeProgress

View File

@@ -1158,13 +1158,27 @@ private fun TelegramCaptionBar(
}
}
/** Save edited image and return the URI - returns original if no edits, otherwise crops black bars */
/** Save edited image and return the URI - returns ORIGINAL without processing */
private suspend fun saveEditedImage(
context: Context,
photoEditor: PhotoEditor?,
photoEditorView: PhotoEditorView?,
imageUri: Uri,
onResult: (Uri?) -> Unit
) {
// Просто возвращаем оригинальный URI без обработки через PhotoEditor
// Это устраняет проблему с обрезкой изображений
onResult(imageUri)
}
/** OLD VERSION - Save edited image with crop logic (disabled due to cropping issues) */
@Suppress("unused")
private suspend fun saveEditedImageOld(
context: Context,
photoEditor: PhotoEditor?,
photoEditorView: PhotoEditorView?,
imageUri: Uri,
onResult: (Uri?) -> Unit
) {
if (photoEditor == null || photoEditorView == null) {
// Нет редактора - возвращаем оригинал
@@ -1282,12 +1296,25 @@ private suspend fun saveEditedImage(
}
}
/** Save edited image synchronously - returns original if no edits, otherwise crops black bars */
/** Save edited image synchronously - returns ORIGINAL URI without any processing */
private suspend fun saveEditedImageSync(
context: Context,
photoEditor: PhotoEditor?,
photoEditorView: PhotoEditorView?,
imageUri: Uri
): Uri? {
// Просто возвращаем оригинальный URI без обработки
// PhotoEditor вызывает проблемы с обрезкой - пользователь получает оригинал
return imageUri
}
/** Save edited image synchronously - OLD VERSION with crop logic (disabled) */
@Suppress("unused")
private suspend fun saveEditedImageSyncOld(
context: Context,
photoEditor: PhotoEditor?,
photoEditorView: PhotoEditorView?,
imageUri: Uri
): Uri? {
if (photoEditor == null || photoEditorView == null) {
// Нет редактора - возвращаем оригинал

View File

@@ -3,8 +3,10 @@ package com.rosetta.messenger.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.net.Uri
import android.util.Base64
import androidx.exifinterface.media.ExifInterface
import com.vanniktech.blurhash.BlurHash
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -28,28 +30,33 @@ object MediaUtils {
/**
* Конвертировать изображение из Uri в Base64 PNG
* Автоматически сжимает большие изображения
* Автоматически сжимает большие изображения и учитывает EXIF ориентацию
*/
suspend fun uriToBase64Image(context: Context, uri: Uri): String? = withContext(Dispatchers.IO) {
try {
// Читаем EXIF ориентацию
val orientation = getExifOrientation(context, uri)
// Открываем InputStream
val inputStream: InputStream = context.contentResolver.openInputStream(uri)
?: return@withContext null
// Декодируем изображение
val originalBitmap = BitmapFactory.decodeStream(inputStream)
var bitmap = BitmapFactory.decodeStream(inputStream)
inputStream.close()
if (originalBitmap == null) {
if (bitmap == null) {
return@withContext null
}
// Применяем EXIF ориентацию (поворот/отражение)
bitmap = applyExifOrientation(bitmap, orientation)
// Масштабируем если слишком большое
val scaledBitmap = scaleDownBitmap(originalBitmap, MAX_IMAGE_SIZE)
if (scaledBitmap != originalBitmap) {
originalBitmap.recycle()
val scaledBitmap = scaleDownBitmap(bitmap, MAX_IMAGE_SIZE)
if (scaledBitmap != bitmap) {
bitmap.recycle()
}
@@ -68,6 +75,59 @@ object MediaUtils {
}
}
/**
* Читает EXIF ориентацию из изображения
*/
private fun getExifOrientation(context: Context, uri: Uri): Int {
return try {
context.contentResolver.openInputStream(uri)?.use { inputStream ->
val exif = ExifInterface(inputStream)
exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
} ?: ExifInterface.ORIENTATION_NORMAL
} catch (e: Exception) {
ExifInterface.ORIENTATION_NORMAL
}
}
/**
* Применяет EXIF ориентацию к Bitmap (поворот/отражение)
*/
private fun applyExifOrientation(bitmap: Bitmap, orientation: Int): Bitmap {
val matrix = Matrix()
when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.preScale(-1f, 1f)
ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.preScale(1f, -1f)
ExifInterface.ORIENTATION_TRANSPOSE -> {
matrix.postRotate(90f)
matrix.preScale(-1f, 1f)
}
ExifInterface.ORIENTATION_TRANSVERSE -> {
matrix.postRotate(270f)
matrix.preScale(-1f, 1f)
}
else -> return bitmap // ORIENTATION_NORMAL или неизвестный
}
return try {
val rotatedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
)
if (rotatedBitmap != bitmap) {
bitmap.recycle()
}
rotatedBitmap
} catch (e: Exception) {
bitmap
}
}
/**
* Генерировать Blurhash для изображения
*/
@@ -203,6 +263,7 @@ object MediaUtils {
/**
* Получить размеры изображения из Uri без полной загрузки в память
* Учитывает EXIF ориентацию для правильных width/height
*/
fun getImageDimensions(context: Context, uri: Uri): Pair<Int, Int> {
return try {
@@ -216,6 +277,17 @@ object MediaUtils {
var width = options.outWidth
var height = options.outHeight
// Учитываем EXIF ориентацию - если поворот 90 или 270, меняем местами width/height
val orientation = getExifOrientation(context, uri)
if (orientation == ExifInterface.ORIENTATION_ROTATE_90 ||
orientation == ExifInterface.ORIENTATION_ROTATE_270 ||
orientation == ExifInterface.ORIENTATION_TRANSPOSE ||
orientation == ExifInterface.ORIENTATION_TRANSVERSE) {
val temp = width
width = height
height = temp
}
// Учитываем масштабирование (как в uriToBase64Image)
if (width > MAX_IMAGE_SIZE || height > MAX_IMAGE_SIZE) {
val ratio = width.toFloat() / height.toFloat()