From fad4e3c01226a4b2d8218c2ea3a4a143168969d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Burak=20Akg=C3=BCn?= Date: Wed, 30 Aug 2023 19:48:33 +0200 Subject: [PATCH] Remove composeMaterialDialogsCore 3rd party, add native dialog compose 1.5.0 --- README-de.md | 1 - README-tr.md | 1 - README.md | 1 - gradle/libs.versions.toml | 1 - shared/build.gradle.kts | 3 - shared/detekt-baseline.xml | 10 +- .../src/commonMain/kotlin/ui/MjImagesApp.kt | 109 +++++++++--------- .../commonMain/kotlin/ui/MjImagesViewModel.kt | 22 +++- 8 files changed, 79 insertions(+), 69 deletions(-) diff --git a/README-de.md b/README-de.md index 77a9975..843d040 100644 --- a/README-de.md +++ b/README-de.md @@ -16,7 +16,6 @@ Die Anwendung wurde im MVVM-Konzept mit Kotlin und Jetpack Compose entwickelt. E - [Kotlinx Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) - [Compose ImageLoader](https://github.com/qdsfdhvh/compose-imageloader) - [KMM-ViewModel](https://github.com/rickclephas/KMM-ViewModel) -- [Compose Material Dialogs](https://github.com/vanpra/compose-material-dialogs) - [Multiplatform Settings](https://github.com/russhwolf/multiplatform-settings)
decisions
diff --git a/README-tr.md b/README-tr.md index 2e3035c..7393278 100644 --- a/README-tr.md +++ b/README-tr.md @@ -17,7 +17,6 @@ Kotlin ve Jetpack Compose kullanılarak MVVM konseptinde geliştirtirildi. Netwo - [Kotlinx Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) - [Compose ImageLoader](https://github.com/qdsfdhvh/compose-imageloader) - [KMM-ViewModel](https://github.com/rickclephas/KMM-ViewModel) -- [Compose Material Dialogs](https://github.com/vanpra/compose-material-dialogs) - [Multiplatform Settings](https://github.com/russhwolf/multiplatform-settings)
decisions
diff --git a/README.md b/README.md index 9ce8976..b1945a0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ Application developed in the MVVM concept using Kotlin and Jetpack Compose. Netw - [Kotlinx Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) - [Compose ImageLoader](https://github.com/qdsfdhvh/compose-imageloader) - [KMM-ViewModel](https://github.com/rickclephas/KMM-ViewModel) -- [Compose Material Dialogs](https://github.com/vanpra/compose-material-dialogs) - [Multiplatform Settings](https://github.com/russhwolf/multiplatform-settings)
decisions
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 738adeb..1f46301 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,6 @@ ktorSerializationKotlinxJson = { module = "io.ktor:ktor-serialization-kotlinx-js kotlinxCoroutinesCore = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref="kotlinxCoroutines" } kotlinxCoroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref="kotlinxCoroutines" } kotlinxSerializationCore = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version="1.6.0" } -composeMaterialDialogsCore = { module = "ca.gosyer:compose-material-dialogs-core", version="0.9.3" } multiplatformSettings = { module = "com.russhwolf:multiplatform-settings-no-arg", version="1.0.0" } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index f02579e..5595155 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -51,9 +51,6 @@ kotlin { //imageloading implementation(libs.imageLoader) - // KMP Dialogs - implementation(libs.composeMaterialDialogsCore) - //coroutines implementation(libs.kotlinxCoroutinesCore) diff --git a/shared/detekt-baseline.xml b/shared/detekt-baseline.xml index d88de6e..2fd4a46 100644 --- a/shared/detekt-baseline.xml +++ b/shared/detekt-baseline.xml @@ -7,12 +7,12 @@ FunctionNaming:MjImagesApp.kt$@Composable fun DraggableThemeSelection( useDarkTheme: Boolean, onClick: (Boolean) -> Unit ) FunctionNaming:MjImagesApp.kt$@Composable fun EmptyScreen( onRefresh: () -> Unit ) FunctionNaming:MjImagesApp.kt$@Composable fun ErrorScreen( onRefresh: () -> Unit ) - FunctionNaming:MjImagesApp.kt$@Composable fun MjImageItem( image: MjImage, height: Dp, contentScale: ContentScale, onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -> Unit, ) - FunctionNaming:MjImagesApp.kt$@Composable fun MjImagesList( images: MjImages, state: LazyStaggeredGridState, onLoadMore: () -> Unit, onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -> Unit, ) - FunctionNaming:MjImagesApp.kt$@Composable fun PreviewDialog( isPreviewVisible: Boolean, imageUrl: String, dialogState: MaterialDialogState = rememberMaterialDialogState() ) + FunctionNaming:MjImagesApp.kt$@Composable fun MjImagesList( images: MjImages, state: LazyStaggeredGridState, onLoadMore: () -> Unit, showPreviewDialog: (imageUrl: String) -> Unit, ) + FunctionNaming:MjImagesApp.kt$@Composable fun PlatformSpecificMjImagesGrid( state: LazyStaggeredGridState, images: MjImages, showPreviewDialog: (imageUrl: String) -> Unit, onLoadMore: () -> Unit, modifier: Modifier = Modifier, ) + FunctionNaming:MjImagesApp.kt$@Composable fun PreviewDialog( imageUrl: String, onDismissed: () -> Unit, ) FunctionNaming:MjImagesApp.kt$@Composable fun PreviewImage(imageUrl: String) FunctionNaming:MjImagesApp.kt$@Composable fun ScrollToTopButton( onClick: () -> Unit, modifier: Modifier = Modifier ) - FunctionNaming:MjImagesApp.kt$@OptIn(ExperimentalFoundationApi::class) @Composable fun PlatformSpecificMjImagesGrid( state: LazyStaggeredGridState, images: MjImages, onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -> Unit, onLoadMore: () -> Unit, modifier: Modifier = Modifier, ) + FunctionNaming:MjImagesApp.kt$@OptIn(ExperimentalFoundationApi::class) @Composable fun MjImageItem( image: MjImage, height: Dp, contentScale: ContentScale, showPreviewDialog: (imageUrl: String) -> Unit, ) FunctionNaming:MjImagesApp.kt$@OptIn(ExperimentalMaterialApi::class) @Composable fun MjImagesApp( viewModel: MjImagesViewModel ) FunctionNaming:Theme.kt$@Composable fun AppTheme( useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) FunctionNaming:main.ios.kt$fun MainViewController(viewModel: MjImagesViewModel): UIViewController @@ -35,8 +35,6 @@ MagicNumber:ImageLoader.kt$1024 MagicNumber:MjImagesApp.kt$.2f MagicNumber:MjImagesApp.kt$.8f - MagicNumber:MjImagesApp.kt$180 - MagicNumber:MjImagesApp.kt$200 MagicNumber:MjImagesApp.kt$230f MagicNumber:MjImagesApp.kt$24f UnusedPrivateProperty:build.gradle.kts$val androidInstrumentedTest by getting { dependencies { implementation(libs.androidxUiTestJunit4) implementation(libs.androidxUiTestManifest) } } diff --git a/shared/src/commonMain/kotlin/ui/MjImagesApp.kt b/shared/src/commonMain/kotlin/ui/MjImagesApp.kt index a19abaa..f1fe208 100644 --- a/shared/src/commonMain/kotlin/ui/MjImagesApp.kt +++ b/shared/src/commonMain/kotlin/ui/MjImagesApp.kt @@ -9,8 +9,8 @@ import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -38,6 +38,7 @@ import androidx.compose.material.ScaffoldState import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material.icons.rounded.DarkMode import androidx.compose.material.icons.rounded.LightMode @@ -61,7 +62,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.BlurEffect -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale @@ -70,19 +70,16 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Popup +import androidx.compose.ui.window.PopupProperties import com.seiko.imageloader.LocalImageLoader import com.seiko.imageloader.model.ImageResult import com.seiko.imageloader.rememberImageAction import com.seiko.imageloader.rememberImageActionPainter -import com.vanpra.composematerialdialogs.MaterialDialog -import com.vanpra.composematerialdialogs.MaterialDialogProperties -import com.vanpra.composematerialdialogs.MaterialDialogState -import com.vanpra.composematerialdialogs.rememberMaterialDialogState import domain.model.MjImage import domain.model.MjImages import domain.model.State import kotlin.math.roundToInt -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import ui.theme.AppTheme import util.OnBottomReached @@ -101,6 +98,7 @@ fun MjImagesApp( val images: MjImages by viewModel.images.collectAsState() val state: State by viewModel.state.collectAsState() + val previewUrl by viewModel.dialogPreviewUrl.collectAsState() val onRefresh = viewModel::refreshImages val scaffoldState: ScaffoldState = rememberScaffoldState() @@ -136,8 +134,8 @@ fun MjImagesApp( onLoadMore = viewModel::loadMore, images = images, state = listState, - ) { isPreviewVisible, imageUrl -> - PreviewDialog(isPreviewVisible, imageUrl) + ) { imageUrl -> + viewModel.showPreviewDialog(imageUrl) } } PullRefreshIndicator( @@ -163,6 +161,13 @@ fun MjImagesApp( useDarkTheme, viewModel::setDarkMode ) + + if (previewUrl.isNotEmpty()) { + PreviewDialog( + imageUrl = previewUrl, + onDismissed = viewModel::dismissPreviewDialog + ) + } } } } @@ -174,23 +179,22 @@ fun MjImagesList( images: MjImages, state: LazyStaggeredGridState, onLoadMore: () -> Unit, - onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -> Unit, + showPreviewDialog: (imageUrl: String) -> Unit, ) { PlatformSpecificMjImagesGrid( onLoadMore = onLoadMore, images = images, modifier = Modifier.fillMaxSize().testTag("imagesGrid"), - onPreviewVisibilityChanged = onPreviewVisibilityChanged, + showPreviewDialog = showPreviewDialog, state = state, ) } -@OptIn(ExperimentalFoundationApi::class) @Composable fun PlatformSpecificMjImagesGrid( state: LazyStaggeredGridState, images: MjImages, - onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -> Unit, + showPreviewDialog: (imageUrl: String) -> Unit, onLoadMore: () -> Unit, modifier: Modifier = Modifier, ) { @@ -204,49 +208,34 @@ fun PlatformSpecificMjImagesGrid( key = MjImage::imageUrl ) { image -> MjImageItem( - image, - (180 * image.ratio).dp, - ContentScale.Crop, - onPreviewVisibilityChanged, + image = image, + height = (180 * image.ratio).dp, + contentScale = ContentScale.Crop, + showPreviewDialog = showPreviewDialog, ) } } } +@OptIn(ExperimentalFoundationApi::class) @Composable fun MjImageItem( image: MjImage, height: Dp, contentScale: ContentScale, - onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -> Unit, + showPreviewDialog: (imageUrl: String) -> Unit, ) { val uriHandler = LocalUriHandler.current - var isLongPressed by remember { mutableStateOf(false) } - - if (isLongPressed) { - onPreviewVisibilityChanged.invoke(true, image.imageUrl) - } else { - onPreviewVisibilityChanged.invoke(false, image.imageUrl) - } Surface( modifier = Modifier .padding(4.dp) .fillMaxWidth() - .pointerInput(Unit) { - detectTapGestures( - onTap = { - if (!isLongPressed) - uriHandler.openUri(image.imageUrl) - }, - onPress = { - delay(200) - isLongPressed = true - tryAwaitRelease() - isLongPressed = false - }) - }, + .combinedClickable( + onClick = { uriHandler.openUri(image.imageUrl) }, + onLongClick = { showPreviewDialog.invoke(image.imageUrl) }, + ), elevation = 8.dp, shape = RoundedCornerShape(8.dp) ) { @@ -332,23 +321,36 @@ fun ErrorScreen( @Composable fun PreviewDialog( - isPreviewVisible: Boolean, imageUrl: String, - dialogState: MaterialDialogState = rememberMaterialDialogState() + onDismissed: () -> Unit, ) { - if (isPreviewVisible) { - MaterialDialog( - dialogState = dialogState, - backgroundColor = Color.Transparent, - elevation = 0.dp, - properties = MaterialDialogProperties( - resizable = false - ) - ) { PreviewImage(imageUrl) } - LaunchedEffect(imageUrl) { - dialogState.show() + Popup( + onDismissRequest = { + onDismissed() + }, + properties = PopupProperties( + focusable = true + ), + content = { + Box(contentAlignment = Alignment.TopEnd) { + PreviewImage(imageUrl) + Button( + onClick = { onDismissed() }, + modifier = Modifier + .padding(24.dp) + .shadow(10.dp, shape = CircleShape) + .clip(shape = CircleShape) + .size(50.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = MaterialTheme.colors.background, + contentColor = MaterialTheme.colors.onSurface, + ) + ) { + Icon(Icons.Filled.Close, "close") + } + } } - } + ) } @Composable @@ -365,6 +367,7 @@ fun PreviewImage(imageUrl: String) { Image( painter = painter, contentDescription = null, + contentScale = ContentScale.Crop, modifier = Modifier.graphicsLayer { val animatedValue: Float = .8f + (.2f * transition) val blurValue: Float = @@ -373,7 +376,7 @@ fun PreviewImage(imageUrl: String) { scaleY = animatedValue alpha = animatedValue renderEffect = BlurEffect(blurValue, blurValue) - }.padding(24.dp).fillMaxSize() + }.fillMaxSize() ) } diff --git a/shared/src/commonMain/kotlin/ui/MjImagesViewModel.kt b/shared/src/commonMain/kotlin/ui/MjImagesViewModel.kt index ef9e4e6..d97ba6d 100644 --- a/shared/src/commonMain/kotlin/ui/MjImagesViewModel.kt +++ b/shared/src/commonMain/kotlin/ui/MjImagesViewModel.kt @@ -8,6 +8,7 @@ import domain.usecase.MjImagesFetchUseCase import domain.usecase.MjImagesUseCase import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -20,13 +21,16 @@ class MjImagesViewModel( ) : KMMViewModel() { private val _state = MutableStateFlow(State.LOADING) - val state: StateFlow = _state + val state: StateFlow = _state.asStateFlow() private val _images = MutableStateFlow(MjImages()) - val images: StateFlow = _images + val images: StateFlow = _images.asStateFlow() private val _useDarkTheme: MutableStateFlow = MutableStateFlow(false) - val useDarkTheme: StateFlow = _useDarkTheme + val useDarkTheme: StateFlow = _useDarkTheme.asStateFlow() + + private val _dialogPreviewUrl: MutableStateFlow = MutableStateFlow("") + val dialogPreviewUrl: StateFlow = _dialogPreviewUrl.asStateFlow() init { checkTheme() @@ -84,4 +88,16 @@ class MjImagesViewModel( _useDarkTheme.emit(useCase.isDarkModeEnabled()) } } + + fun showPreviewDialog(imageUrl: String) { + viewModelScope.coroutineScope.launch { + _dialogPreviewUrl.emit(imageUrl) + } + } + + fun dismissPreviewDialog() { + viewModelScope.coroutineScope.launch { + _dialogPreviewUrl.emit("") + } + } }