diff --git a/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin b/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin index 9599610..b9ac55e 100644 Binary files a/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin and b/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin differ diff --git a/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock b/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock index 0719227..383c813 100644 Binary files a/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock and b/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock differ diff --git a/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin b/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin index cf668aa..8a35dc8 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin and b/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin differ diff --git a/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock b/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock index a89b87f..07ef58d 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock and b/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock differ diff --git a/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin b/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin index 6bf986b..ad99699 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin and b/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin differ diff --git a/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 87092d5..24e97c5 100644 Binary files a/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin b/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin index 2c10606..9172f5f 100644 Binary files a/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin and b/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/client-mobile/core/presentation/components/AppMediaLightbox.kt b/client-mobile/core/presentation/components/AppMediaLightbox.kt index 0a1e76f..baf1c6a 100644 --- a/client-mobile/core/presentation/components/AppMediaLightbox.kt +++ b/client-mobile/core/presentation/components/AppMediaLightbox.kt @@ -3,7 +3,11 @@ package core.presentation.components import androidx.compose.animation.* import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.calculatePan +import androidx.compose.foundation.gestures.calculateZoom import androidx.compose.foundation.gestures.detectTransformGestures +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.rememberTransformableState import androidx.compose.foundation.gestures.transformable import androidx.compose.foundation.layout.* @@ -22,6 +26,7 @@ import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties @@ -43,6 +48,7 @@ fun AppMediaLightbox( val context = LocalContext.current val scope = rememberCoroutineScope() val pagerState = rememberPagerState(initialPage = initialIndex, pageCount = { mediaList.size }) + var canScroll by remember { mutableStateOf(true) } Dialog( onDismissRequest = onClose, @@ -60,7 +66,8 @@ fun AppMediaLightbox( state = pagerState, modifier = Modifier.fillMaxSize(), pageSpacing = 16.dp, - beyondBoundsPageCount = 1 + beyondBoundsPageCount = 1, + userScrollEnabled = canScroll ) { page -> val media = mediaList[page] @@ -79,22 +86,78 @@ fun AppMediaLightbox( } else if (media.type.startsWith("image") || media.type.contains("gif")) { var scale by remember { mutableFloatStateOf(1f) } var offset by remember { mutableStateOf(androidx.compose.ui.geometry.Offset.Zero) } + + // Reset zoom when page changes + LaunchedEffect(pagerState.currentPage) { + scale = 1f + offset = androidx.compose.ui.geometry.Offset.Zero + canScroll = true + } - AsyncImage( - model = media.url, - imageLoader = core.utils.CoilUtils.getGifImageLoader(LocalContext.current), - contentDescription = null, + Box( modifier = Modifier .fillMaxSize() - .graphicsLayer( - scaleX = scale, - scaleY = scale, - translationX = offset.x, - translationY = offset.y - ), - contentScale = ContentScale.Fit - ) - } else { + .pointerInput(Unit) { + detectTapGestures( + onDoubleTap = { + if (scale > 1f) { + scale = 1f + offset = Offset.Zero + canScroll = true + } else { + scale = 3f + canScroll = false + } + } + ) + } + .pointerInput(Unit) { + awaitEachGesture { + do { + val event = awaitPointerEvent() + val zoomActive = scale > 1f || event.changes.size > 1 + + if (zoomActive) { + // Если мы в режиме зума - поглощаем события + event.changes.forEach { it.consume() } + + // Сами вычисляем трансформацию + val zoomChange = event.calculateZoom() + val panChange = event.calculatePan() + + scale = (scale * zoomChange).coerceIn(1f, 5f) + canScroll = scale <= 1f + + if (scale > 1f) { + offset += panChange + } else { + offset = Offset.Zero + } + } + // Если zoomActive == false, мы НЕ вызываем consume(), + // и событие уходит наверх в HorizontalPager + } while (event.changes.any { it.pressed }) + } + }, + contentAlignment = Alignment.Center + ) { + AsyncImage( + model = media.url, + imageLoader = core.utils.CoilUtils.getGifImageLoader(LocalContext.current), + contentDescription = null, + modifier = Modifier + .fillMaxSize() + .graphicsLayer( + scaleX = scale, + scaleY = scale, + translationX = offset.x, + translationY = offset.y + ), + contentScale = ContentScale.Fit + ) + } + } + else { // Document / File placeholder in Lightbox Column( modifier = Modifier.fillMaxSize().padding(32.dp),