diff --git a/build.gradle.kts b/build.gradle.kts index 474b868eda..661fc9d30a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,7 +30,7 @@ tasks.register("clean").configure { delete(rootProject.layout.buildDirectory) } -private val ktLintVersion = the().versions.ktlint.get() +private val catalog = the() allprojects { // Detekt @@ -46,7 +46,7 @@ allprojects { config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { - detektPlugins("io.nlopez.compose.rules:detekt:0.5.8") + detektPlugins(catalog.detekt.compose.rules) detektPlugins(project(":tests:detekt-rules")) } @@ -64,7 +64,7 @@ allprojects { // See https://github.com/JLLeitschuh/ktlint-gradle#configuration configure { - version = ktLintVersion + version = catalog.versions.ktlint.get() android = true ignoreFailures = false enableExperimentalRules = true diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt index 5f55beaf60..a563520e09 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt @@ -10,6 +10,7 @@ package io.element.android.features.home.impl.components import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -160,7 +161,7 @@ fun HomeTopBar( } @Composable -private fun RoomListMenuItems( +private fun RowScope.RoomListMenuItems( onToggleSearch: () -> Unit, onMenuActionClick: (RoomListMenuAction) -> Unit, canReportBug: Boolean, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt index 1f0d6ff7d9..8a270f1b85 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt @@ -48,6 +48,7 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -93,7 +94,7 @@ fun SpaceFiltersView( SpaceFiltersBottomSheetContent( filters = state.visibleFilters, searchQuery = state.searchQuery, - onFilterSelected = { filter -> + onSelectFilter = { filter -> state.eventSink(SpaceFiltersEvent.Selecting.SelectFilter(filter)) } ) @@ -105,9 +106,9 @@ fun SpaceFiltersView( @Composable private fun SpaceFiltersBottomSheetContent( - filters: List, + filters: ImmutableList, searchQuery: TextFieldState, - onFilterSelected: (SpaceServiceFilter) -> Unit, + onSelectFilter: (SpaceServiceFilter) -> Unit, modifier: Modifier = Modifier ) { Column( @@ -133,7 +134,7 @@ private fun SpaceFiltersBottomSheetContent( items(filters) { filter -> SpaceFilterItem( filter = filter, - onClick = { onFilterSelected(filter) } + onClick = { onSelectFilter(filter) } ) } } diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt index 4db2aa9ad5..cc94951dbd 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt @@ -147,7 +147,7 @@ private fun DoneButton( } @Composable -private fun Buttons( +private fun ColumnScope.Buttons( onRetry: () -> Unit, onCancel: () -> Unit, ) { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationPinMarkers.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationPinMarkers.kt index 762a719cf4..e30dfd53de 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationPinMarkers.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationPinMarkers.kt @@ -14,6 +14,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.api.Location import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.components.rememberLocationPinBitmap +import kotlinx.collections.immutable.ImmutableList import kotlinx.serialization.json.JsonPrimitive import org.maplibre.compose.expressions.dsl.and import org.maplibre.compose.expressions.dsl.asString @@ -66,7 +67,7 @@ data class LocationMarkerData( */ @Composable fun LocationPinMarkers( - markers: List, + markers: ImmutableList, onMarkerClick: ((LocationMarkerData) -> Unit)? = null, onClusterClick: ((Position) -> Unit)? = null, ) { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index 985c6c8bdc..52400cd49d 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -180,6 +181,7 @@ private fun StartLiveLocationActionView( onActionSuccess: () -> Unit, modifier: Modifier = Modifier, ) { + val updatedOnActionSuccess by rememberUpdatedState(onActionSuccess) Box(modifier = modifier) { val asyncIndicatorState = rememberAsyncIndicatorState() AsyncIndicatorHost(state = asyncIndicatorState) @@ -202,7 +204,7 @@ private fun StartLiveLocationActionView( } } is AsyncAction.Success -> { - LaunchedEffect(action) { onActionSuccess() } + LaunchedEffect(action) { updatedOnActionSuccess() } } else -> Unit } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 80ffaabedd..231bc01b12 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -50,6 +50,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.launch import org.maplibre.compose.camera.CameraMoveReason import org.maplibre.compose.camera.CameraPosition @@ -172,7 +173,7 @@ fun ShowLocationView( trackUserLocation = state.isTrackMyLocation ) val markers = remember(state.locationShares) { - state.locationShares.map { it.toMarkerData() } + state.locationShares.map { it.toMarkerData() }.toImmutableList() } LocationPinMarkers(markers) }, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt index a6d9da3de2..6b5da8f300 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt @@ -125,7 +125,7 @@ private fun Content(errorScreenType: QrCodeErrorScreenType) { } @Composable -private fun Buttons( +private fun ColumnScope.Buttons( onRetry: () -> Unit, onCancel: () -> Unit, ) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 3e6f14e805..b299ea42a9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.consumeWindowInsets @@ -412,7 +413,7 @@ fun MessagesView( } @Composable -internal fun MessagesMenuActions( +internal fun RowScope.MessagesMenuActions( displayThreads: Boolean, roomCallState: RoomCallState, onJoinCallClick: (isAudioCall: Boolean) -> Unit, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt index 5e26d849a5..7d741eb3ea 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt @@ -28,6 +28,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -157,6 +158,7 @@ private fun ScrollHelper( listState: LazyListState, onPaginate: () -> Unit, ) { + val updatedOnPaginate by rememberUpdatedState(onPaginate) val lastVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex + listState.layoutInfo.visibleItemsInfo.size - 1 } } @@ -168,7 +170,7 @@ private fun ScrollHelper( } LaunchedEffect(needsPagination, lastVisibleItemIndex) { if (needsPagination) { - onPaginate() + updatedOnPaginate() delay(400L) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContent.kt index 1003d03b4b..12368530a3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContent.kt @@ -9,7 +9,9 @@ package io.element.android.features.messages.impl.timeline.model.event import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState +import androidx.compose.runtime.rememberUpdatedState import io.element.android.features.location.api.Location import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -103,8 +105,9 @@ private fun rememberIsLiveLocationActive( mode: TimelineItemLocationContent.Mode.Live, currentTimeMillis: () -> Long, ): Boolean { + val updatedCurrentTimeMillis by rememberUpdatedState(currentTimeMillis) fun TimelineItemLocationContent.Mode.Live.isActive(): Boolean { - return isActive && endTimestamp > currentTimeMillis() + return isActive && endTimestamp > updatedCurrentTimeMillis() } return produceState( initialValue = mode.isActive(), @@ -112,7 +115,7 @@ private fun rememberIsLiveLocationActive( key2 = mode.isActive, ) { if (mode.isActive) { - val remainingMillis = mode.endTimestamp - currentTimeMillis() + val remainingMillis = mode.endTimestamp - updatedCurrentTimeMillis() delay(remainingMillis) } value = false diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index af28e3443f..c108c45d3b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -206,7 +206,7 @@ fun AdvancedSettingsView( if (state.liveLocationMinimumDistanceUpdate != null) { LiveLocationUpdatesSection( value = state.liveLocationMinimumDistanceUpdate, - onValueSaved = { value -> + onSaveValue = { value -> state.eventSink(AdvancedSettingsEvents.SetLiveLocationMinimumDistanceUpdate(value)) }, onOpenAppPermissionsClick = onOpenAppSettingsClick, @@ -339,7 +339,7 @@ private fun ModerationAndSafety( @Composable private fun LiveLocationUpdatesSection( value: Int, - onValueSaved: (Int) -> Unit, + onSaveValue: (Int) -> Unit, onOpenAppPermissionsClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -382,7 +382,7 @@ private fun LiveLocationUpdatesSection( value = sliderValue.toFloat(), onValueChange = { sliderValue = it.roundToInt() }, onValueChangeFinish = { - onValueSaved(sliderValue) + onSaveValue(sliderValue) }, valueRange = valueRange, colors = SliderDefaults.colors( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index b30c73b9c8..40210279fa 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -206,7 +206,7 @@ fun RoomDetailsView( ), trailingContent = ListItemContent.Custom { Box( - modifier = modifier + modifier = Modifier .size(8.dp) .clip(CircleShape) .background(ElementTheme.colors.iconAccentPrimary) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceNode.kt index dadd54cf6f..c7aea55488 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceNode.kt @@ -41,7 +41,7 @@ class AddRoomToSpaceNode( AddRoomToSpaceView( state = state, onBackClick = callback::onFinish, - onRoomsAdded = callback::onFinish, + onAddRoom = callback::onFinish, modifier = modifier ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt index c64efa8074..47c78e6fb8 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt @@ -23,6 +23,8 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter @@ -57,9 +59,10 @@ import kotlinx.collections.immutable.toImmutableList fun AddRoomToSpaceView( state: AddRoomToSpaceState, onBackClick: () -> Unit, - onRoomsAdded: () -> Unit, + onAddRoom: () -> Unit, modifier: Modifier = Modifier, ) { + val updatedOnAddRoom by rememberUpdatedState(onAddRoom) fun onRoomToggled(room: SelectRoomInfo) { state.eventSink(AddRoomToSpaceEvent.ToggleRoom(room)) } @@ -78,7 +81,7 @@ fun AddRoomToSpaceView( // Navigate back on success LaunchedEffect(state.saveAction) { if (state.saveAction is AsyncAction.Success) { - onRoomsAdded() + updatedOnAddRoom() } } @@ -247,6 +250,6 @@ internal fun AddRoomToSpaceViewPreview( AddRoomToSpaceView( state = state, onBackClick = {}, - onRoomsAdded = {}, + onAddRoom = {}, ) } diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt index 6fc10f1e82..fa47ce63bb 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt @@ -125,7 +125,7 @@ private fun AndroidComposeUiTest.setAddRoomToSpaceView( AddRoomToSpaceView( state = state, onBackClick = onBackClick, - onRoomsAdded = onRoomsAdded, + onAddRoom = onRoomsAdded, ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b526c0ab69..f3e79e3c45 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,6 +61,8 @@ autoservice = "1.1.1" # quality detekt = "1.23.8" +# Stick to version < 0.5 since error detection is broken. See https://mrmans0n.github.io/compose-rules/detekt/#supported-versions-matrix +detekt_compose_rules = "0.4.28" # See https://github.com/pinterest/ktlint/releases/ ktlint = "1.8.0" androidx-test-ext-junit = "1.3.0" @@ -72,6 +74,8 @@ android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref compose_compiler_plugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } # https://developer.android.com/studio/write/java8-support#library-desugaring-versions android_desugar = "com.android.tools:desugar_jdk_libs:2.1.5" +# Used in build.gradle.kts +detekt_compose_rules = { module = "io.nlopez.compose.rules:detekt", version.ref = "detekt_compose_rules" } kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } metro_gradle_plugin = { module = "dev.zacsweers.metro:gradle-plugin", version.ref = "metro" } kotlin_compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin" } diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/SafeChildrenTransitionScope.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/SafeChildrenTransitionScope.kt index 00e49d7bee..b90e95beca 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/SafeChildrenTransitionScope.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/SafeChildrenTransitionScope.kt @@ -186,6 +186,7 @@ class SafeChildrenTransitionScope( } @OptIn(ExperimentalSharedTransitionApi::class) +@Suppress("ContentSlotReused") @Composable inline fun ParentNode.SafeChildren( navModel: NavModel, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt index 689cba727f..ec5f7ce456 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt @@ -24,6 +24,8 @@ import androidx.compose.material3.SheetState import androidx.compose.material3.contentColorFor import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.movableContentOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape @@ -88,14 +90,15 @@ fun ModalBottomSheet( dragHandle = dragHandle, contentWindowInsets = contentWindowInsets, ) { + val movableContent = remember { movableContentOf { content() } } if (scrollable) { Column( modifier = Modifier.verticalScroll(rememberScrollState()), ) { - content() + movableContent() } } else { - content() + movableContent() } } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt index 4d3ba4dc6b..81822a04cc 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt @@ -184,6 +184,7 @@ private fun PickButton( } } +@Suppress("ModifierClickableOrder") @Composable private fun BoxScope.OverlayEditButton( onClick: () -> Unit, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt index b0b81bbde0..6feb3c7d53 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt @@ -13,8 +13,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.ProduceStateScope import androidx.compose.runtime.State +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState +import androidx.compose.runtime.rememberUpdatedState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.matrix.api.core.EventId @@ -87,8 +89,9 @@ class MediaViewerDataSource( fun produceState( producer: suspend ProduceStateScope>.(StateFlow>) -> Unit ): State> { + val latestProducer by rememberUpdatedState(producer) return produceState(initialValue = initialData()) { - producer(dataFlow) + latestProducer(dataFlow) } } diff --git a/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt b/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt index 18bd1cff10..cae7452cd8 100644 --- a/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt +++ b/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt @@ -18,6 +18,7 @@ import androidx.camera.view.PreviewView import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -114,7 +115,8 @@ fun QrCodeCameraView( Box(modifier.clipToBounds()) { if (LocalInspectionMode.current) { Box( - modifier = modifier + modifier = Modifier + .fillMaxSize() .background(color = ElementTheme.colors.bgSubtlePrimary), contentAlignment = Alignment.Center, ) { diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 3993b3d64c..0f62eb5234 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -32,6 +32,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -419,7 +420,7 @@ fun TextComposer( // Re-focus the text input when voice recording ends so the user can continue typing var previousVoiceMessageState by remember { mutableStateOf(voiceMessageState) } - LaunchedEffect(voiceMessageState) { + LaunchedEffect(voiceMessageState, onRequestFocus) { if (voiceMessageState is VoiceMessageState.Idle && previousVoiceMessageState !is VoiceMessageState.Idle) { onRequestFocus() } @@ -522,6 +523,7 @@ private fun StandardLayout( .padding(bottom = 8.dp, top = 8.dp) .weight(1f) ) { + val movableVoiceRecording = remember { movableContentOf { voiceRecording() } } if (voiceMessageState is VoiceMessageState.Idle) { textInput() } else if (composerMode is MessageComposerMode.Special) { @@ -530,10 +532,10 @@ private fun StandardLayout( onResetComposerMode = onResetComposerMode, isTextEmpty = true, ) { - voiceRecording() + movableVoiceRecording() } } else { - voiceRecording() + movableVoiceRecording() } } // To avoid loosing keyboard focus, the IconButton has to be defined here and has to be always enabled. diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 552b46755e..cc99ecab5a 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -150,7 +150,7 @@ exceptions: TooGenericExceptionCaught: active: false SwallowedException: - active: true + active: false ThrowingExceptionsWithoutMessageOrCause: active: true TooGenericExceptionThrown: @@ -224,12 +224,27 @@ comments: Compose: CompositionLocalAllowlist: active: true + # You can optionally define a list of CompositionLocals that are allowed here + allowedCompositionLocals: + - LocalCompoundColors + - LocalSnackbarDispatcher + - LocalCameraPositionState + - LocalMediaItemPresenterFactories + - LocalTimelineItemPresenterFactories + - LocalRoomMemberProfilesCache + - LocalMentionSpanUpdater + - LocalAnalyticsService + - LocalBuildMeta + - LocalUiTestMode + - LocalSdkIntVersionProvider CompositionLocalNaming: active: true ContentEmitterReturningValues: active: true # You can optionally add your own composables here # contentEmitters: MyComposable,MyOtherComposable + ModifierComposable: + active: true ModifierMissing: active: true ModifierReused: @@ -252,6 +267,8 @@ Compose: active: true PreviewPublic: active: true + # You can optionally disable that only previews with @PreviewParameter are flagged + previewPublicOnlyIfParams: false RememberMissing: active: true UnstableCollections: