forked from melod1n/fast-messenger
Refactor: Use Dialog for PhotoViewScreen
This commit refactors the PhotoViewScreen to be displayed as a Dialog instead of a separate navigation destination.
Key changes:
- Introduced `PhotoViewDialog` composable that wraps `PhotoViewRoute` in a `FullScreenDialog`.
- Modified `RootScreen` to use `PhotoViewDialog` for displaying images.
- Updated `PhotoViewViewModelImpl` to handle loading state and display a loader while downloading images.
- Made `Loader` and `ContainedLoader` colors configurable.
- Adjusted `PhotoViewScreen` UI:
- Set background to translucent black.
- Updated TopAppBar background color and icon tints.
- Improved vertical drag gesture for dismissing the viewer.
- Made `VkUserData.LastSeen.platform` nullable.
- Removed unused navigation functions related to the old PhotoViewScreen.
This commit is contained in:
@@ -7,6 +7,8 @@ import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
@@ -14,6 +16,10 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
@@ -35,8 +41,7 @@ import dev.meloda.fast.messageshistory.navigation.messagesHistoryScreen
|
||||
import dev.meloda.fast.messageshistory.navigation.navigateToMessagesHistory
|
||||
import dev.meloda.fast.navigation.Main
|
||||
import dev.meloda.fast.navigation.mainScreen
|
||||
import dev.meloda.fast.photoviewer.navigation.navigateToPhotoView
|
||||
import dev.meloda.fast.photoviewer.navigation.photoViewScreen
|
||||
import dev.meloda.fast.photoviewer.presentation.PhotoViewDialog
|
||||
import dev.meloda.fast.settings.navigation.navigateToSettings
|
||||
import dev.meloda.fast.settings.navigation.settingsScreen
|
||||
import dev.meloda.fast.ui.R
|
||||
@@ -120,6 +125,11 @@ fun RootScreen(
|
||||
LocalNavRootController provides navController,
|
||||
LocalNavController provides navController
|
||||
) {
|
||||
var photoViewerInfo by rememberSaveable {
|
||||
mutableStateOf<Pair<List<String>, Int?>?>(null)
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = requireNotNull(startDestination),
|
||||
@@ -139,7 +149,7 @@ fun RootScreen(
|
||||
onError = viewModel::onError,
|
||||
onSettingsButtonClicked = navController::navigateToSettings,
|
||||
onNavigateToMessagesHistory = navController::navigateToMessagesHistory,
|
||||
onPhotoClicked = { url -> navController.navigateToPhotoView(listOf(url)) },
|
||||
onPhotoClicked = { url -> photoViewerInfo = listOf(url) to null },
|
||||
onMessageClicked = navController::navigateToMessagesHistory,
|
||||
onNavigateToCreateChat = navController::navigateToCreateChat
|
||||
)
|
||||
@@ -148,11 +158,13 @@ fun RootScreen(
|
||||
onError = viewModel::onError,
|
||||
onBack = navController::navigateUp,
|
||||
onNavigateToChatMaterials = navController::navigateToChatMaterials,
|
||||
onNavigateToPhotoViewer = navController::navigateToPhotoView
|
||||
onNavigateToPhotoViewer = { photos, index ->
|
||||
photoViewerInfo = photos to index
|
||||
}
|
||||
)
|
||||
chatMaterialsScreen(
|
||||
onBack = navController::navigateUp,
|
||||
onPhotoClicked = { url -> navController.navigateToPhotoView(listOf(url)) }
|
||||
onPhotoClicked = { url -> photoViewerInfo = listOf(url) to null }
|
||||
)
|
||||
createChatScreen(
|
||||
onChatCreated = { conversationId ->
|
||||
@@ -176,8 +188,12 @@ fun RootScreen(
|
||||
}
|
||||
)
|
||||
languagePickerScreen(onBack = navController::navigateUp)
|
||||
}
|
||||
|
||||
photoViewScreen(onBack = navController::navigateUp)
|
||||
PhotoViewDialog(
|
||||
photoViewerInfo = photoViewerInfo,
|
||||
onDismiss = { photoViewerInfo = null }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ data class VkUserData(
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class LastSeen(
|
||||
@Json(name = "platform") val platform: Int,
|
||||
@Json(name = "platform") val platform: Int?,
|
||||
@Json(name = "time") val time: Int
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package dev.meloda.fast.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.compose.ui.zIndex
|
||||
|
||||
@Composable
|
||||
fun FullScreenDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = {},
|
||||
DialogProperties(
|
||||
usePlatformDefaultWidth = false,
|
||||
decorFitsSystemWindows = false
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(10F),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,52 +10,70 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun FullScreenContainedLoader(modifier: Modifier = Modifier) {
|
||||
fun FullScreenContainedLoader(
|
||||
modifier: Modifier = Modifier,
|
||||
containerColor: Color = MaterialTheme.colorScheme.primary,
|
||||
indicatorColor: Color = MaterialTheme.colorScheme.primaryContainer
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
ContainedLoader()
|
||||
ContainedLoader(
|
||||
containerColor = containerColor,
|
||||
indicatorColor = indicatorColor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun FullScreenLoader(modifier: Modifier = Modifier) {
|
||||
fun FullScreenLoader(
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = MaterialTheme.colorScheme.primary
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Loader()
|
||||
Loader(color = color)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
@Preview
|
||||
fun ContainedLoader(modifier: Modifier = Modifier) {
|
||||
fun ContainedLoader(
|
||||
modifier: Modifier = Modifier,
|
||||
containerColor: Color = MaterialTheme.colorScheme.primary,
|
||||
indicatorColor: Color = MaterialTheme.colorScheme.primaryContainer
|
||||
) {
|
||||
ContainedLoadingIndicator(
|
||||
modifier = modifier,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
indicatorColor = MaterialTheme.colorScheme.primaryContainer
|
||||
containerColor = containerColor,
|
||||
indicatorColor = indicatorColor
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
@Preview
|
||||
fun Loader(modifier: Modifier = Modifier) {
|
||||
fun Loader(
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = MaterialTheme.colorScheme.primary
|
||||
) {
|
||||
LoadingIndicator(
|
||||
modifier = modifier,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = color
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+28
-5
@@ -17,6 +17,7 @@ import coil.imageLoader
|
||||
import coil.request.ImageRequest
|
||||
import dev.meloda.fast.common.extensions.setValue
|
||||
import dev.meloda.fast.common.model.UiImage
|
||||
import dev.meloda.fast.photoviewer.model.PhotoViewArguments
|
||||
import dev.meloda.fast.photoviewer.model.PhotoViewScreenState
|
||||
import dev.meloda.fast.photoviewer.navigation.PhotoView
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -47,17 +48,22 @@ interface PhotoViewViewModel {
|
||||
}
|
||||
|
||||
class PhotoViewViewModelImpl(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
arguments: PhotoViewArguments,
|
||||
private val applicationContext: Context
|
||||
) : PhotoViewViewModel, ViewModel() {
|
||||
|
||||
override val screenState = MutableStateFlow(PhotoViewScreenState.EMPTY)
|
||||
constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
applicationContext: Context
|
||||
) : this(
|
||||
arguments = PhotoView.from(savedStateHandle).arguments,
|
||||
applicationContext = applicationContext
|
||||
)
|
||||
|
||||
override val screenState = MutableStateFlow(PhotoViewScreenState.EMPTY)
|
||||
override val shareRequest = MutableStateFlow<Uri?>(null)
|
||||
|
||||
init {
|
||||
val arguments = PhotoView.from(savedStateHandle).arguments
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
images = arguments.imageUrls
|
||||
@@ -165,12 +171,18 @@ class PhotoViewViewModelImpl(
|
||||
}
|
||||
|
||||
private suspend fun downloadAndStoreImageToCache(url: String): File? =
|
||||
runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
screenState.setValue { old -> old.copy(isLoading = true) }
|
||||
|
||||
val drawable = applicationContext.imageLoader.execute(
|
||||
ImageRequest.Builder(applicationContext)
|
||||
.data(url)
|
||||
.build()
|
||||
).drawable ?: return@withContext null
|
||||
).drawable ?: run {
|
||||
screenState.setValue { old -> old.copy(isLoading = false) }
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
val imagesDir = File(applicationContext.cacheDir, "images")
|
||||
if (!imagesDir.exists()) imagesDir.mkdirs()
|
||||
@@ -181,4 +193,15 @@ class PhotoViewViewModelImpl(
|
||||
|
||||
imageFile
|
||||
}
|
||||
}.fold(
|
||||
onSuccess = { file ->
|
||||
screenState.setValue { old -> old.copy(isLoading = false) }
|
||||
file
|
||||
},
|
||||
onFailure = { e ->
|
||||
e.printStackTrace()
|
||||
screenState.setValue { old -> old.copy(isLoading = false) }
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,15 @@ package dev.meloda.fast.photoviewer.di
|
||||
|
||||
import dev.meloda.fast.photoviewer.PhotoViewViewModel
|
||||
import dev.meloda.fast.photoviewer.PhotoViewViewModelImpl
|
||||
import org.koin.core.module.dsl.viewModelOf
|
||||
import org.koin.core.module.dsl.viewModel
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
|
||||
val photoViewModule = module {
|
||||
viewModelOf(::PhotoViewViewModelImpl) bind PhotoViewViewModel::class
|
||||
viewModel {
|
||||
PhotoViewViewModelImpl(
|
||||
savedStateHandle = get(),
|
||||
applicationContext = get()
|
||||
)
|
||||
} bind PhotoViewViewModel::class
|
||||
}
|
||||
|
||||
+4
-2
@@ -6,13 +6,15 @@ import dev.meloda.fast.common.model.UiImage
|
||||
@Immutable
|
||||
data class PhotoViewScreenState(
|
||||
val images: List<UiImage>,
|
||||
val selectedPage: Int
|
||||
val selectedPage: Int,
|
||||
val isLoading: Boolean
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val EMPTY: PhotoViewScreenState = PhotoViewScreenState(
|
||||
images = emptyList(),
|
||||
selectedPage = 0
|
||||
selectedPage = 0,
|
||||
isLoading = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
-27
@@ -1,15 +1,10 @@
|
||||
package dev.meloda.fast.photoviewer.navigation
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.toRoute
|
||||
import dev.meloda.fast.photoviewer.model.PhotoViewArguments
|
||||
import dev.meloda.fast.photoviewer.presentation.PhotoViewRoute
|
||||
import dev.meloda.fast.ui.extensions.customNavType
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.net.URLEncoder
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
@Serializable
|
||||
@@ -21,25 +16,3 @@ data class PhotoView(val arguments: PhotoViewArguments) {
|
||||
savedStateHandle.toRoute<PhotoView>(typeMap)
|
||||
}
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.photoViewScreen(
|
||||
onBack: () -> Unit
|
||||
) {
|
||||
composable<PhotoView>(typeMap = PhotoView.typeMap) {
|
||||
PhotoViewRoute(onBack = onBack)
|
||||
}
|
||||
}
|
||||
|
||||
fun NavController.navigateToPhotoView(
|
||||
images: List<String>,
|
||||
selectedIndex: Int? = null
|
||||
) {
|
||||
this.navigate(
|
||||
PhotoView(
|
||||
arguments = PhotoViewArguments(
|
||||
imageUrls = images.map { URLEncoder.encode(it, "utf-8") },
|
||||
selectedIndex = selectedIndex
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+88
-39
@@ -2,8 +2,13 @@ package dev.meloda.fast.photoviewer.presentation
|
||||
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.draggable
|
||||
import androidx.compose.foundation.gestures.rememberDraggableState
|
||||
@@ -24,7 +29,6 @@ import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
@@ -40,32 +44,65 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.painter.ColorPainter
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil.compose.AsyncImage
|
||||
import com.conena.nanokt.android.content.pxToDp
|
||||
import dev.meloda.fast.common.model.UiImage
|
||||
import dev.meloda.fast.photoviewer.PhotoViewViewModel
|
||||
import dev.meloda.fast.photoviewer.PhotoViewViewModelImpl
|
||||
import dev.meloda.fast.photoviewer.model.PhotoViewArguments
|
||||
import dev.meloda.fast.photoviewer.model.PhotoViewScreenState
|
||||
import dev.meloda.fast.ui.components.FullScreenDialog
|
||||
import dev.meloda.fast.ui.components.Loader
|
||||
import dev.meloda.fast.ui.util.getImage
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import java.net.URLEncoder
|
||||
import kotlin.math.abs
|
||||
import dev.meloda.fast.ui.R as UiR
|
||||
|
||||
@Composable
|
||||
fun PhotoViewRoute(
|
||||
fun PhotoViewDialog(
|
||||
photoViewerInfo: Pair<List<String>, Int?>?,
|
||||
modifier: Modifier = Modifier,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val applicationContext = LocalContext.current.applicationContext
|
||||
|
||||
if (photoViewerInfo != null) {
|
||||
FullScreenDialog(modifier = modifier) {
|
||||
val viewModel = remember(true) {
|
||||
PhotoViewViewModelImpl(
|
||||
arguments = PhotoViewArguments(
|
||||
imageUrls = photoViewerInfo.first.map {
|
||||
URLEncoder.encode(it, "utf-8")
|
||||
},
|
||||
selectedIndex = photoViewerInfo.second
|
||||
),
|
||||
applicationContext = applicationContext
|
||||
)
|
||||
}
|
||||
|
||||
PhotoViewRoute(
|
||||
onBack = onDismiss,
|
||||
viewModel = viewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PhotoViewRoute(
|
||||
onBack: () -> Unit,
|
||||
viewModel: PhotoViewViewModel = koinViewModel<PhotoViewViewModelImpl>()
|
||||
) {
|
||||
@@ -80,7 +117,7 @@ fun PhotoViewRoute(
|
||||
viewModel.onImageShared()
|
||||
|
||||
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||
setType("image/png")
|
||||
type = "image/png"
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
putExtra(Intent.EXTRA_STREAM, shareRequest)
|
||||
}
|
||||
@@ -116,7 +153,7 @@ fun PhotoViewRoute(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PhotoViewScreen(
|
||||
private fun PhotoViewScreen(
|
||||
screenState: PhotoViewScreenState = PhotoViewScreenState.EMPTY,
|
||||
onBack: () -> Unit = {},
|
||||
onPageChanged: (index: Int) -> Unit = {},
|
||||
@@ -148,7 +185,6 @@ fun PhotoViewScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.graphicsLayer(alpha = calculatedAlpha),
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = onBack,
|
||||
@@ -158,9 +194,7 @@ fun PhotoViewScreen(
|
||||
onCopyLinkClicked = onCopyLinkClicked,
|
||||
)
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.background.copy(
|
||||
alpha = calculatedAlpha
|
||||
)
|
||||
containerColor = Color.Black.copy(alpha = calculatedAlpha)
|
||||
) { padding ->
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Pager(
|
||||
@@ -169,15 +203,34 @@ fun PhotoViewScreen(
|
||||
padding = padding,
|
||||
onBack = onBack,
|
||||
onVerticalDrag = { offset -> offsetY = offset },
|
||||
modifier = Modifier
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = screenState.isLoading,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable(
|
||||
interactionSource = null,
|
||||
indication = null,
|
||||
onClick = {}
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black.copy(alpha = 0.5f)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Loader(color = Color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TopBar(
|
||||
private fun TopBar(
|
||||
modifier: Modifier = Modifier,
|
||||
onBack: () -> Unit,
|
||||
onShareClicked: () -> Unit,
|
||||
@@ -193,12 +246,16 @@ fun TopBar(
|
||||
|
||||
TopAppBar(
|
||||
modifier = modifier,
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = Color.Black.copy(alpha = 0.3f)
|
||||
),
|
||||
title = {},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
||||
contentDescription = "Back button"
|
||||
contentDescription = "Back button",
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -208,7 +265,8 @@ fun TopBar(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.MoreVert,
|
||||
contentDescription = "Options"
|
||||
contentDescription = "Options",
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
@@ -255,13 +313,12 @@ fun TopBar(
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Pager(
|
||||
private fun Pager(
|
||||
modifier: Modifier = Modifier,
|
||||
pagerState: PagerState,
|
||||
state: PhotoViewScreenState,
|
||||
@@ -269,6 +326,8 @@ fun Pager(
|
||||
onBack: () -> Unit,
|
||||
onVerticalDrag: (offset: Float) -> Unit
|
||||
) {
|
||||
val windowInfo = LocalWindowInfo.current
|
||||
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = modifier.fillMaxSize()
|
||||
@@ -289,22 +348,25 @@ fun Pager(
|
||||
} else {
|
||||
var offsetY by remember { mutableFloatStateOf(0f) }
|
||||
|
||||
val animatedOffset by animateFloatAsState(
|
||||
targetValue = offsetY,
|
||||
label = "animatedOffset"
|
||||
)
|
||||
var useAnimatedOffset by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val animatedOffset by animateFloatAsState(
|
||||
targetValue = offsetY,
|
||||
label = "animatedOffset",
|
||||
animationSpec = tween(
|
||||
durationMillis = if (useAnimatedOffset) 150 else 0,
|
||||
easing = LinearEasing
|
||||
)
|
||||
)
|
||||
|
||||
AsyncImage(
|
||||
model = model,
|
||||
contentDescription = "Image",
|
||||
modifier = Modifier
|
||||
.graphicsLayer {
|
||||
this.translationY = if (useAnimatedOffset) {
|
||||
animatedOffset
|
||||
} else offsetY
|
||||
this.translationY = animatedOffset
|
||||
}
|
||||
.draggable(
|
||||
state = rememberDraggableState { delta ->
|
||||
@@ -314,7 +376,7 @@ fun Pager(
|
||||
},
|
||||
orientation = Orientation.Vertical,
|
||||
onDragStopped = {
|
||||
if (abs(offsetY.pxToDp()) >= 200) {
|
||||
if (abs(offsetY) / windowInfo.containerSize.height >= 0.25) {
|
||||
onBack()
|
||||
} else {
|
||||
useAnimatedOffset = true
|
||||
@@ -331,16 +393,3 @@ fun Pager(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PhotoViewScreenPreview() {
|
||||
PhotoViewScreen(
|
||||
screenState = PhotoViewScreenState(
|
||||
images = List(200) {
|
||||
UiImage.Resource(UiR.drawable.test_captcha)
|
||||
},
|
||||
selectedPage = 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user