diff --git a/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt b/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt index 49c2cc5782..52e3af1aab 100644 --- a/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt +++ b/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt @@ -28,7 +28,7 @@ import io.element.android.x.R @Preview @Composable -fun IconPreview( +internal fun IconPreview( modifier: Modifier = Modifier, ) { Box(modifier = modifier) { @@ -39,7 +39,7 @@ fun IconPreview( @Preview @Composable -fun RoundIconPreview( +internal fun RoundIconPreview( modifier: Modifier = Modifier, ) { Box(modifier = modifier.clip(shape = CircleShape)) { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt index 64c9ec7c4f..e55f059d14 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt @@ -58,7 +58,8 @@ class LoggedInEventProcessor @Inject constructor( .filter { it } .onEach { displayMessage(CommonStrings.common_verification_complete) - }.launchIn(this) + } + .launchIn(this) } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index effe67fd49..fe4e31c70d 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -44,14 +44,14 @@ import io.element.android.appnav.loggedin.LoggedInNode import io.element.android.appnav.room.RoomFlowNode import io.element.android.appnav.room.RoomLoadedFlowNode import io.element.android.features.createroom.api.CreateRoomEntryPoint +import io.element.android.features.ftue.api.FtueEntryPoint +import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.invitelist.api.InviteListEntryPoint import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.roomlist.api.RoomListEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint -import io.element.android.features.ftue.api.FtueEntryPoint -import io.element.android.features.ftue.api.state.FtueState import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler @@ -100,13 +100,13 @@ class LoggedInFlowNode @AssistedInject constructor( ) { interface Callback : Plugin { - fun onOpenBugReport() = Unit + fun onOpenBugReport() } interface LifecycleCallback : NodeLifecycleCallback { - fun onFlowCreated(identifier: String, client: MatrixClient) = Unit + fun onFlowCreated(identifier: String, client: MatrixClient) - fun onFlowReleased(identifier: String, client: MatrixClient) = Unit + fun onFlowReleased(identifier: String, client: MatrixClient) } data class Inputs( @@ -305,7 +305,8 @@ class LoggedInFlowNode @AssistedInject constructor( override fun onFtueFlowFinished() { backstack.pop() } - }).build() + }) + .build() } } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index 60784ea4ed..964cb447aa 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -58,7 +58,7 @@ fun LoggedInView( @DayNightPreviews @Composable -fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview { +internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview { LoggedInView( state = state ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt index 5108bb8716..bdec2b528f 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt @@ -91,7 +91,7 @@ private fun SyncState.mustBeVisible() = when (this) { @DayNightPreviews @Composable -fun SyncStateViewPreview() = ElementPreview { +internal fun SyncStateViewPreview() = ElementPreview { // Add a box to see the shadow Box(modifier = Modifier.padding(24.dp)) { SyncStateView( diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt index aa01b259de..558f64424a 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt @@ -103,12 +103,12 @@ private fun LoadingRoomTopBar( @Preview @Composable -fun LoadingRoomNodeViewLightPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = +internal fun LoadingRoomNodeViewLightPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun LoadingRoomNodeViewDarkPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = +internal fun LoadingRoomNodeViewDarkPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index 20ec9f48b4..661d3c5433 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -96,7 +96,8 @@ class RoomFlowNode @AssistedInject constructor( } else { backstack.newRoot(NavTarget.Loading) } - }.launchIn(lifecycleScope) + } + .launchIn(lifecycleScope) } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt index 24ec9795f7..d00c4791f7 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt @@ -75,8 +75,8 @@ class RoomLoadedFlowNode @AssistedInject constructor( } interface LifecycleCallback : NodeLifecycleCallback { - fun onFlowCreated(identifier: String, room: MatrixRoom) = Unit - fun onFlowReleased(identifier: String, room: MatrixRoom) = Unit + fun onFlowCreated(identifier: String, room: MatrixRoom) + fun onFlowReleased(identifier: String, room: MatrixRoom) } data class Inputs( @@ -115,7 +115,8 @@ class RoomLoadedFlowNode @AssistedInject constructor( room.updateMembers() .onFailure { Timber.e(it, "Fail to fetch members for room ${room.roomId}") - }.onSuccess { + } + .onSuccess { Timber.v("Success fetching members for room ${room.roomId}") } } diff --git a/build.gradle.kts b/build.gradle.kts index 9272514899..a50b7673e4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,7 +56,7 @@ allprojects { // activate all available (even unstable) rules. allRules = true // point to your custom config defining rules to run, overwriting default behavior - config = files("$rootDir/tools/detekt/detekt.yml") + config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { detektPlugins("io.nlopez.compose.rules:detekt:0.1.12") diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt index f6d77226b9..5aa9287d86 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt +++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt @@ -81,12 +81,12 @@ fun buildAnnotatedStringWithColoredPart( @Preview @Composable -fun AnalyticsPreferencesViewLightPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = +internal fun AnalyticsPreferencesViewLightPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun AnalyticsPreferencesViewDarkPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = +internal fun AnalyticsPreferencesViewDarkPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index a27e6e7399..5453daa0f9 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -204,13 +204,13 @@ private fun AnalyticsOptInFooter( @Preview @Composable -fun AnalyticsOptInViewLightPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewLight { +internal fun AnalyticsOptInViewLightPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun AnalyticsOptInViewDarkPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewDark { +internal fun AnalyticsOptInViewDarkPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewDark { ContentToPreview(state) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt index 1d900cea8c..302f72d514 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt @@ -93,11 +93,11 @@ fun RoomPrivacyOption( @Preview @Composable -fun RoomPrivacyOptionLightPreview() = ElementPreviewLight { ContentToPreview() } +internal fun RoomPrivacyOptionLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun RoomPrivacyOptionDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun RoomPrivacyOptionDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable private fun ContentToPreview() { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 37fcdbb7b1..c964fa1d57 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -282,12 +282,12 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = @Preview @Composable -fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = +internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = +internal fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt index d1484b7a4f..7a3bf6ef03 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt @@ -41,7 +41,7 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider { get() = sequenceOf( aWaitListState(loginAction = Async.Uninitialized), aWaitListState(loginAction = Async.Loading()), - aWaitListState(loginAction = Async.Failure(Throwable())), + aWaitListState(loginAction = Async.Failure(Throwable("error"))), aWaitListState(loginAction = Async.Failure(Throwable(message = "IO_ELEMENT_X_WAIT_LIST"))), aWaitListState(loginAction = Async.Success(SessionId("@alice:element.io"))), // Add other state here diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index acaaf54c9e..8818cc6476 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -71,6 +71,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType +import io.element.android.libraries.matrix.ui.room.canRedactAsState import io.element.android.libraries.matrix.ui.room.canSendMessageAsState import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.coroutines.CoroutineScope @@ -109,6 +110,7 @@ class MessagesPresenter @AssistedInject constructor( val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) + val userHasPermissionToRedact by room.canRedactAsState(updateKey = syncUpdateFlow.value) var roomName: Async by remember { mutableStateOf(Async.Uninitialized) } var roomAvatar: Async by remember { mutableStateOf(Async.Uninitialized) } LaunchedEffect(syncUpdateFlow.value) { @@ -165,6 +167,7 @@ class MessagesPresenter @AssistedInject constructor( roomName = roomName, roomAvatar = roomAvatar, userHasPermissionToSendMessage = userHasPermissionToSendMessage, + userHasPermissionToRedact = userHasPermissionToRedact, composerState = composerState, timelineState = timelineState, actionListState = actionListState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index a042ec1ac4..50e952f237 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -33,6 +33,7 @@ data class MessagesState( val roomName: Async, val roomAvatar: Async, val userHasPermissionToSendMessage: Boolean, + val userHasPermissionToRedact: Boolean, val composerState: MessageComposerState, val timelineState: TimelineState, val actionListState: ActionListState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index ce50cc138b..bb0b61f620 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -50,6 +50,7 @@ fun aMessagesState() = MessagesState( roomName = Async.Success("Room name"), roomAvatar = Async.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)), userHasPermissionToSendMessage = true, + userHasPermissionToRedact = false, composerState = aMessageComposerState().copy( text = "Hello", isFullScreen = false, 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 f909ac7f1a..4d93810e20 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 @@ -115,7 +115,7 @@ fun MessagesView( fun onMessageLongClicked(event: TimelineItem.Event) { Timber.v("OnMessageLongClicked= ${event.id}") localView.hideKeyboard() - state.actionListState.eventSink(ActionListEvents.ComputeForMessage(event)) + state.actionListState.eventSink(ActionListEvents.ComputeForMessage(event, state.userHasPermissionToRedact)) } fun onActionSelected(action: TimelineItemAction, event: TimelineItem.Event) { @@ -127,8 +127,9 @@ fun MessagesView( state.eventSink(MessagesEvents.ToggleReaction(emoji, event.eventId)) } - fun onMoreReactionsClicked(event: TimelineItem.Event): Unit = + fun onMoreReactionsClicked(event: TimelineItem.Event) { state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event.eventId)) + } Scaffold( modifier = modifier, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt index a6244a72e3..3c796036e7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt @@ -20,5 +20,5 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem sealed interface ActionListEvents { object Clear : ActionListEvents - data class ComputeForMessage(val event: TimelineItem.Event) : ActionListEvents + data class ComputeForMessage(val event: TimelineItem.Event, val canRedact: Boolean) : ActionListEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 2d18018746..f71c750c22 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -28,6 +28,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.canBeCopied +import io.element.android.features.messages.impl.timeline.model.event.canReact import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta import kotlinx.collections.immutable.toImmutableList @@ -48,13 +49,20 @@ class ActionListPresenter @Inject constructor( } val displayEmojiReactions by remember { - derivedStateOf { (target.value as? ActionListState.Target.Success)?.event?.isRemote == true } + derivedStateOf { + val event = (target.value as? ActionListState.Target.Success)?.event + event?.isRemote == true && event.content.canReact() + } } fun handleEvents(event: ActionListEvents) { when (event) { ActionListEvents.Clear -> target.value = ActionListState.Target.None - is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage(event.event, target) + is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage( + timelineItem = event.event, + userCanRedact = event.canRedact, + target = target, + ) } } @@ -65,7 +73,11 @@ class ActionListPresenter @Inject constructor( ) } - private fun CoroutineScope.computeForMessage(timelineItem: TimelineItem.Event, target: MutableState) = launch { + private fun CoroutineScope.computeForMessage( + timelineItem: TimelineItem.Event, + userCanRedact: Boolean, + target: MutableState + ) = launch { target.value = ActionListState.Target.Loading(timelineItem) val actions = when (timelineItem.content) { @@ -102,7 +114,7 @@ class ActionListPresenter @Inject constructor( if (!timelineItem.isMine) { add(TimelineItemAction.ReportContent) } - if (timelineItem.isMine) { + if (timelineItem.isMine || userCanRedact) { add(TimelineItemAction.Redact) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index fd2ad94345..613caba586 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -405,7 +405,7 @@ private fun EmojiButton( @DayNightPreviews @Composable -fun SheetContentPreview( +internal fun SheetContentPreview( @PreviewParameter(ActionListStateProvider::class) state: ActionListState ) = ElementPreview { SheetContent( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index ee41ace4b0..de7f5cd47b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -30,7 +30,7 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider Unit, modifier: Modifier = Mo @Preview @Composable -fun ForwardMessagesViewLightPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = +internal fun ForwardMessagesViewLightPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun ForwardMessagesViewDarkPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = +internal fun ForwardMessagesViewDarkPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt index 7cc73ef32e..13a9ff3bee 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt @@ -103,14 +103,17 @@ class MediaViewerPresenter @AssistedInject constructor( ) .onSuccess { mediaFile.value = it - }.mapCatching { mediaFile -> + } + .mapCatching { mediaFile -> localMediaFactory.createFromMediaFile( mediaFile = mediaFile, mediaInfo = inputs.mediaInfo ) - }.onSuccess { + } + .onSuccess { localMedia.value = Async.Success(it) - }.onFailure { + } + .onFailure { localMedia.value = Async.Failure(it) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt index 820a34d8d4..1042261be8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt @@ -32,7 +32,7 @@ open class MediaViewerStateProvider : PreviewParameterProvider get() = sequenceOf( aMediaViewerState(), aMediaViewerState(Async.Loading()), - aMediaViewerState(Async.Failure(IllegalStateException())), + aMediaViewerState(Async.Failure(IllegalStateException("error"))), aMediaViewerState( Async.Success( LocalMedia(Uri.EMPTY, anImageInfo()) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt index 5fb14a6ca0..ff2e4d49f4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt @@ -255,7 +255,7 @@ private fun ErrorView( @Preview @Composable -fun MediaViewerViewDarkPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) = +internal fun MediaViewerViewDarkPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index ec16d3342d..934a67f2e4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -268,7 +268,8 @@ class MessageComposerPresenter @Inject constructor( mediaSender.sendMedia(uri, mimeType, compressIfPossible = false, progressCallback) .onSuccess { attachmentState.value = AttachmentsState.None - }.onFailure { + } + .onFailure { val snackbarMessage = SnackbarMessage(sendAttachmentError(it)) snackbarDispatcher.post(snackbarMessage) attachmentState.value = AttachmentsState.None diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt index 89e6d7a220..de5e787a3d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt @@ -26,7 +26,7 @@ open class ReportMessageStateProvider : PreviewParameterProvider true else -> false } + +/** + * Return true if user can react (i.e. send a reaction) on the event content. + */ +fun TimelineItemEventContent.canReact(): Boolean = + when (this) { + is TimelineItemTextBasedContent, + is TimelineItemAudioContent, + is TimelineItemEncryptedContent, + is TimelineItemFileContent, + is TimelineItemImageContent, + is TimelineItemLocationContent, + is TimelineItemVideoContent -> true + is TimelineItemStateContent, + is TimelineItemRedactedContent, + TimelineItemUnknownContent -> false + } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index b47ac55d35..c19dbe7cdc 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -44,8 +44,11 @@ import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper +import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.media.MediaSource @@ -83,9 +86,16 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = consumeItemsUntilTimeout().last() assertThat(initialState.roomId).isEqualTo(A_ROOM_ID) + assertThat(initialState.roomName).isEqualTo(Async.Success("")) + assertThat(initialState.roomAvatar).isEqualTo(Async.Success(AvatarData(id = A_ROOM_ID.value, name = "", size = AvatarSize.TimelineRoom))) + assertThat(initialState.userHasPermissionToSendMessage).isTrue() + assertThat(initialState.userHasPermissionToRedact).isFalse() + assertThat(initialState.hasNetworkConnection).isTrue() + assertThat(initialState.snackbarMessage).isNull() + assertThat(initialState.inviteProgress).isEqualTo(Async.Uninitialized) + assertThat(initialState.showReinvitePrompt).isFalse() } } @@ -531,6 +541,19 @@ class MessagesPresenterTest { } } + @Test + fun `present - permission to redact`() = runTest { + val matrixRoom = FakeMatrixRoom(canRedact = true) + val presenter = createMessagePresenter(matrixRoom = matrixRoom) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = consumeItemsUntilPredicate { it.userHasPermissionToRedact }.last() + assertThat(initialState.userHasPermissionToRedact).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + private fun TestScope.createMessagePresenter( coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(), matrixRoom: MatrixRoom = FakeMatrixRoom(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt index a2baa9dff7..afef9f6730 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt @@ -56,7 +56,7 @@ class ActionListPresenterTest { }.test { val initialState = awaitItem() val messageEvent = aMessageEvent(isMine = true, content = TimelineItemRedactedContent) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -81,7 +81,7 @@ class ActionListPresenterTest { }.test { val initialState = awaitItem() val messageEvent = aMessageEvent(isMine = false, content = TimelineItemRedactedContent) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -109,7 +109,7 @@ class ActionListPresenterTest { isMine = false, content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -130,6 +130,37 @@ class ActionListPresenterTest { } } + @Test + fun `present - compute for others message and can redact`() = runTest { + val presenter = anActionListPresenter(isBuildDebuggable = true) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val messageEvent = aMessageEvent( + isMine = false, + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) + ) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, true)) + val successState = awaitItem() + assertThat(successState.target).isEqualTo( + ActionListState.Target.Success( + messageEvent, + persistentListOf( + TimelineItemAction.Reply, + TimelineItemAction.Forward, + TimelineItemAction.Copy, + TimelineItemAction.Developer, + TimelineItemAction.ReportContent, + TimelineItemAction.Redact, + ) + ) + ) + initialState.eventSink.invoke(ActionListEvents.Clear) + assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None) + } + } + @Test fun `present - compute for my message`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) @@ -141,7 +172,7 @@ class ActionListPresenterTest { isMine = true, content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -174,7 +205,7 @@ class ActionListPresenterTest { isMine = true, content = aTimelineItemImageContent(), ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -205,7 +236,7 @@ class ActionListPresenterTest { isMine = true, content = aTimelineItemStateEventContent(), ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -234,7 +265,7 @@ class ActionListPresenterTest { isMine = true, content = aTimelineItemStateEventContent(), ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -262,7 +293,7 @@ class ActionListPresenterTest { isMine = true, content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -299,10 +330,10 @@ class ActionListPresenterTest { content = TimelineItemRedactedContent, ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) assertThat(awaitItem().target).isInstanceOf(ActionListState.Target.Success::class.java) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(redactedEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(redactedEvent, false)) awaitItem().run { assertThat(target).isEqualTo(ActionListState.Target.None) assertThat(displayEmojiReactions).isFalse() @@ -323,7 +354,7 @@ class ActionListPresenterTest { content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false), ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) val successState = awaitItem() assertThat(successState.target).isEqualTo( ActionListState.Target.Success( diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt index bf05dbc5f5..855bf067dd 100644 --- a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt +++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt @@ -44,8 +44,10 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @@ -85,14 +87,14 @@ private fun Indicator(modifier: Modifier = Modifier) { .statusBarsPadding() .padding(vertical = 6.dp), horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.Bottom, + verticalAlignment = Alignment.CenterVertically, ) { val tint = MaterialTheme.colorScheme.primary Image( imageVector = Icons.Outlined.WifiOff, contentDescription = null, colorFilter = ColorFilter.tint(tint), - modifier = Modifier.size(16.dp), + modifier = Modifier.size(16.sp.toDp()), ) Spacer(modifier = Modifier.width(8.dp)) Text( diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt index b7c2663b72..81075969c6 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt @@ -50,12 +50,12 @@ fun AboutView( @Preview @Composable -fun AboutViewLightPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = +internal fun AboutViewLightPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun AboutViewDarkPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = +internal fun AboutViewDarkPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt index 165406c6f5..3ee7365122 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt @@ -46,12 +46,12 @@ fun AnalyticsSettingsView( @Preview @Composable -fun AnalyticsSettingsViewLightPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = +internal fun AnalyticsSettingsViewLightPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun AnalyticsSettingsViewDarkPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = +internal fun AnalyticsSettingsViewDarkPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 7c5b5d91c8..23ea4faf86 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -96,12 +96,12 @@ fun FeatureListContent( @Preview @Composable -fun DeveloperSettingsViewLightPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = +internal fun DeveloperSettingsViewLightPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun DeveloperSettingsViewDarkPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = +internal fun DeveloperSettingsViewDarkPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 90eade31ab..ba8dc05f35 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -129,12 +129,12 @@ fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) { @LargeHeightPreview @Composable -fun PreferencesRootViewLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = +internal fun PreferencesRootViewLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreviewLight { ContentToPreview(matrixUser) } @LargeHeightPreview @Composable -fun PreferencesRootViewDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = +internal fun PreferencesRootViewDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreviewDark { ContentToPreview(matrixUser) } @Composable diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt index 73e04fb5d4..3cc2a8211a 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt @@ -68,12 +68,12 @@ fun RageshakePreferencesView( @Preview @Composable -fun RageshakePreferencesViewLightPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = +internal fun RageshakePreferencesViewLightPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun RageshakePreferencesViewDarkPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = +internal fun RageshakePreferencesViewDarkPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 8b66d1db09..095f85e9c7 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -178,11 +178,11 @@ fun BugReportView( @Preview @Composable -fun BugReportViewLightPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewLight { ContentToPreview(state) } +internal fun BugReportViewLightPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun BugReportViewDarkPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewDark { ContentToPreview(state) } +internal fun BugReportViewDarkPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewDark { ContentToPreview(state) } @Composable private fun ContentToPreview(state: BugReportState) { diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt index a5e7edf405..b71c8af372 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt @@ -62,9 +62,9 @@ class VectorUncaughtExceptionHandler( totalSize = info.totalMemory() usedSize = totalSize - freeSize } - append("usedSize " + usedSize / 1048576L + " MB\n") - append("freeSize " + freeSize / 1048576L + " MB\n") - append("totalSize " + totalSize / 1048576L + " MB\n") + append("usedSize " + usedSize / 1_048_576L + " MB\n") + append("freeSize " + freeSize / 1_048_576L + " MB\n") + append("totalSize " + totalSize / 1_048_576L + " MB\n") append("Thread: ") append(thread.name) append(", Exception: ") 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 1e5d1a8ebb..3ec597e525 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 @@ -17,13 +17,11 @@ package io.element.android.features.roomdetails.impl import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -226,22 +224,28 @@ internal fun RoomHeaderSection( roomAlias: String?, modifier: Modifier = Modifier ) { - Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { - Box(modifier = Modifier.size(70.dp)) { - Avatar( - avatarData = AvatarData(roomId, roomName, avatarUrl, AvatarSize.RoomHeader), - modifier = Modifier.fillMaxSize() - ) - } + Column( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Avatar( + avatarData = AvatarData(roomId, roomName, avatarUrl, AvatarSize.RoomHeader), + modifier = Modifier.size(70.dp) + ) Spacer(modifier = Modifier.height(24.dp)) - Text(roomName, style = ElementTheme.typography.fontHeadingLgBold) + Text( + text = roomName, + style = ElementTheme.typography.fontHeadingLgBold, + textAlign = TextAlign.Center, + ) if (roomAlias != null) { Spacer(modifier = Modifier.height(6.dp)) Text( text = roomAlias, style = ElementTheme.typography.fontBodyLgRegular, color = MaterialTheme.colorScheme.secondary, - modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), textAlign = TextAlign.Center, ) } @@ -328,12 +332,12 @@ internal fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = M @LargeHeightPreview @Composable -fun RoomDetailsLightPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = +internal fun RoomDetailsLightPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = ElementPreviewLight { ContentToPreview(state) } @LargeHeightPreview @Composable -fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = +internal fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index fb384dcdd5..14a3f38d2f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -292,12 +292,12 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = @Preview @Composable -fun RoomDetailsEditViewLightPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = +internal fun RoomDetailsEditViewLightPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun RoomDetailsEditViewDarkPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = +internal fun RoomDetailsEditViewDarkPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt index 555f04af20..b53800236e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt @@ -220,12 +220,12 @@ private fun RoomInviteMembersSearchBar( @Preview @Composable -fun RoomInviteMembersLightPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = +internal fun RoomInviteMembersLightPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun RoomInviteMembersDarkPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = +internal fun RoomInviteMembersDarkPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index 3bfde66c06..bad1dc3591 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -257,12 +257,12 @@ private fun RoomMemberSearchBar( @Preview @Composable -fun RoomMemberListLightPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = +internal fun RoomMemberListLightPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun RoomMemberListDarkPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = +internal fun RoomMemberListDarkPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt index fdf8a9d5a4..84dd9319cf 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt @@ -153,12 +153,12 @@ internal fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier = @LargeHeightPreview @Composable -fun RoomMemberDetailsViewLightPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) = +internal fun RoomMemberDetailsViewLightPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) = ElementPreviewLight { ContentToPreview(state) } @LargeHeightPreview @Composable -fun RoomMemberDetailsViewDarkPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) = +internal fun RoomMemberDetailsViewDarkPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index db0ea8c11d..c144ef8671 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -43,6 +43,8 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.text.applyScaleDown +import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem @@ -114,7 +116,11 @@ private fun DefaultRoomListTopBar( val fontStyle = if (scrollBehavior.state.collapsedFraction > 0.5) ElementTheme.typography.aliasScreenTitle else - ElementTheme.typography.fontHeadingLgBold + ElementTheme.typography.fontHeadingLgBold.copy( + // Due to a limitation of MediumTopAppBar, and to avoid the text to be truncated, + // ensure that the font size will never be bigger than 28.dp. + fontSize = 28.dp.applyScaleDown().toSp() + ) Text( style = fontStyle, text = stringResource(id = R.string.screen_roomlist_main_space_title) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index 8602f15910..4eb7bc9d72 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -73,7 +73,8 @@ class RoomListDataSource @Inject constructor( } .onEach { _filteredRooms.value = it - }.launchIn(coroutineScope) + } + .launchIn(coroutineScope) } fun updateFilter(filterValue: String) { diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index 77984a3bb2..a116300dc6 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -242,12 +242,12 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) @Preview @Composable -fun VerifySelfSessionViewLightPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = +internal fun VerifySelfSessionViewLightPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun VerifySelfSessionViewDarkPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = +internal fun VerifySelfSessionViewDarkPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff37e483d8..98767036ee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ telephoto = "0.4.0" # DI dagger = "2.47" -anvil = "2.4.6" +anvil = "2.4.7-1-8" # Auto service autoservice = "1.1.1" diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt index 8347990303..a9a17dcceb 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt @@ -183,7 +183,7 @@ fun Context.startInstallFromSourceIntent( noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found), ) { val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES) - .setData(Uri.parse(String.format("package:%s", packageName))) + .setData(Uri.parse("package:$packageName")) try { activityResultLauncher.launch(intent) } catch (activityNotFoundException: ActivityNotFoundException) { diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt index b96d9e166b..534c9d741b 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt @@ -23,5 +23,5 @@ import com.bumble.appyx.core.plugin.plugins interface NodeInputs : Plugin inline fun Node.inputs(): I { - return plugins().firstOrNull() ?: throw RuntimeException("Make sure to actually pass NodeInputs plugin to your node") + return requireNotNull(plugins().firstOrNull()) { "Make sure to actually pass NodeInputs plugin to your node" } } diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt index db07432df0..343f5ce351 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt @@ -72,7 +72,3 @@ fun String.ellipsize(length: Int): String { return "${this.take(length)}…" } - -inline fun Any?.takeAs(): R? { - return takeIf { it is R } as R? -} diff --git a/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt index 483e27af71..5aefcdcd7b 100644 --- a/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt +++ b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt @@ -101,7 +101,7 @@ class DefaultLastMessageTimestampFormatterTest { * Create DefaultLastMessageFormatter and set current time to the provided date. */ private fun createFormatter(@Suppress("SameParameterValue") currentDate: String): LastMessageTimestampFormatter { - val clock = FakeClock().also { it.givenInstant(Instant.parse(currentDate)) } + val clock = FakeClock().apply { givenInstant(Instant.parse(currentDate)) } val localDateTimeProvider = LocalDateTimeProvider(clock, TimeZone.UTC) val dateFormatters = DateFormatters(Locale.US, clock, TimeZone.UTC) return DefaultLastMessageTimestampFormatter(localDateTimeProvider, dateFormatters) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt index 6b20c96880..2af9e77b99 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt @@ -70,7 +70,7 @@ fun InfoListItemMolecule( @DayNightPreviews @Composable -fun InfoListItemMoleculePreview() { +internal fun InfoListItemMoleculePreview() { ElementPreview { val color = if (isSystemInDarkTheme()) Color.DarkGray else Color.LightGray Column( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt index 19cefaed26..4856d54bff 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt @@ -68,11 +68,11 @@ fun LabelledTextField( @Preview @Composable -fun LabelledTextFieldLightPreview() = ElementPreviewLight { ContentToPreview() } +internal fun LabelledTextFieldLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun LabelledTextFieldDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun LabelledTextFieldDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable private fun ContentToPreview() { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt index c6af3e1cdf..93a0c5d436 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt @@ -51,6 +51,6 @@ fun PinIcon( @DayNightPreviews @Composable -fun PinIconPreview() = ElementPreview { +internal fun PinIconPreview() = ElementPreview { PinIcon() } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt index b28e52a5ff..1c763a81c3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt @@ -102,7 +102,7 @@ private fun InitialsAvatar( @Preview(group = PreviewGroup.Avatars) @Composable -fun AvatarPreview(@PreviewParameter(AvatarDataProvider::class) avatarData: AvatarData) = +internal fun AvatarPreview(@PreviewParameter(AvatarDataProvider::class) avatarData: AvatarData) = ElementThemedPreview { Row( verticalAlignment = Alignment.CenterVertically, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt index 1727fffd1c..14c7ad3eff 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt @@ -20,19 +20,16 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider open class AvatarDataProvider : PreviewParameterProvider { override val values: Sequence - get() { - AvatarSize.values() - .also { it.sortBy { item -> item.name } } - .asSequence() - return AvatarSize.values().asSequence().map { + get() = AvatarSize.values() + .asSequence() + .map { sequenceOf( anAvatarData(size = it), anAvatarData(size = it).copy(name = null), anAvatarData(size = it).copy(url = "aUrl"), ) } - .flatten() - } + .flatten() } fun anAvatarData( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt index 201d6f7151..b91e6a1024 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt @@ -42,6 +42,13 @@ const val DAY_MODE_NAME = "D" * * NB: Content should be wrapped into [ElementPreview] to apply proper theming. */ -@Preview(name = DAY_MODE_NAME) -@Preview(name = NIGHT_MODE_NAME, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview( + name = DAY_MODE_NAME, + fontScale = 1f, +) +@Preview( + name = NIGHT_MODE_NAME, + uiMode = Configuration.UI_MODE_NIGHT_YES, + fontScale = 1f, +) annotation class DayNightPreviews diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt new file mode 100644 index 0000000000..6d3ecfc82b --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.preview + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Density + +/** + * Showkase does not take into account the `fontScale` parameter of the Preview annotation, so alter the + * LocalDensity in the CompositionLocalProvider. + */ +@Composable +fun WithFontScale(fontScale: Float, content: @Composable () -> Unit) { + CompositionLocalProvider( + LocalDensity provides Density( + density = LocalDensity.current.density, + fontScale = fontScale + ) + ) { + content() + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt new file mode 100644 index 0000000000..7a4f5dd172 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.text + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.preview.WithFontScale +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.theme.ElementTheme + +/** + * Return the maximum value between the receiver value and the value with fontScale applied. + * So if fontScale is >= 1f, the same value is returned, and if fontScale is < 1f, so returned value + * will be smaller. + */ +@Composable +fun Dp.applyScaleDown(): Dp = with(LocalDensity.current) { + return this@applyScaleDown * fontScale.coerceAtMost(1f) +} + +/** + * Return the minimum value between the receiver value and the value with fontScale applied. + * So if fontScale is <= 1f, the same value is returned, and if fontScale is > 1f, so returned value + * will be bigger. + */ +@Composable +fun Dp.applyScaleUp(): Dp = with(LocalDensity.current) { + return this@applyScaleUp * fontScale.coerceAtLeast(1f) +} + +@Preview +@Composable +fun DpScalePreview_0_75f() = WithFontScale(0.75f) { + ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "Text with size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "Text with the same size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "Text with a smaller size (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } + } +} + +@Preview +@Composable +fun DpScalePreview_1_0f() = WithFontScale(1f) { + ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "Text with size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "Text with the same size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "Text with the same size (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } + } +} + +@Preview +@Composable +fun DpScalePreview_1_5f() = WithFontScale(1.5f) { + ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "Text with size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "Text with a bigger size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "Text with the same size (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt index a422c5e729..685fe40302 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt @@ -44,7 +44,7 @@ internal fun DatePickerPreviewDark() { @Composable private fun ContentToPreview() { val state = rememberDatePickerState( - initialSelectedDateMillis = 1672578000000L, + initialSelectedDateMillis = 1_672_578_000_000L, ) AlertDialogContent( buttons = { /*TODO*/ }, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt index dcbef866fe..2618a5de82 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt @@ -28,10 +28,12 @@ import timber.log.Timber @Composable fun LogCompositions(tag: String, msg: String) { if (BuildConfig.DEBUG) { - val ref = remember { Ref(0) } + val ref = remember { Ref() } SideEffect { ref.value++ } Timber.tag(tag).d("Compositions: $msg ${ref.value}") } } -class Ref(var value: Int) +private class Ref { + var value: Int = 0 +} diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt index c67be52c1c..5af79e7524 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt @@ -236,7 +236,7 @@ private fun MapView.lifecycleObserver(previousState: MutableState { //handled in onDispose } - else -> throw IllegalStateException() + Lifecycle.Event.ON_ANY -> error("ON_ANY should never be used") } previousState.value = event } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt index fc900e4bbb..ba9cdc1e80 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt @@ -95,9 +95,9 @@ object PermalinkParser { return if (signUrl.isNullOrEmpty().not() && email.isNullOrEmpty().not()) { try { val signValidUri = Uri.parse(signUrl) - val identityServerHost = signValidUri.authority ?: throw IllegalArgumentException() - val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException() - val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException() + val identityServerHost = signValidUri.authority ?: throw IllegalArgumentException("missing `authority`") + val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException("missing `token`") + val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException("missing `private_key`") PermalinkData.RoomEmailInviteLink( roomId = identifier, email = email!!, @@ -137,7 +137,8 @@ object PermalinkParser { .parameterList .filter { it.mParameter == "via" - }.map { + } + .map { URLDecoder.decode(it.mValue, "UTF-8") } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 1445f85228..f8b9e6c2c3 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -105,6 +105,8 @@ interface MatrixRoom : Closeable { suspend fun canUserInvite(userId: UserId): Result + suspend fun canUserRedact(userId: UserId): Result + suspend fun canUserSendState(userId: UserId, type: StateEventType): Result suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt index 852401bffc..e0ba452efe 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt @@ -34,3 +34,9 @@ suspend fun MatrixRoom.canSendState(type: StateEventType): Result = can * Shortcut for calling [MatrixRoom.canUserSendMessage] with our own user. */ suspend fun MatrixRoom.canSendMessage(type: MessageEventType): Result = canUserSendMessage(sessionId, type) + +/** + * Shortcut for calling [MatrixRoom.canUserRedact] with our own user. + */ +suspend fun MatrixRoom.canRedact(): Result = canUserRedact(sessionId) + diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 305bcaaba3..89576cad3a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -147,7 +147,8 @@ class RustMatrixClient constructor( if (syncState == SyncState.Running) { onSlidingSyncUpdate() } - }.launchIn(sessionCoroutineScope) + } + .launchIn(sessionCoroutineScope) } override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) { @@ -227,7 +228,8 @@ class RustMatrixClient constructor( roomSummaryDataSource.allRooms() .filter { roomSummaries -> roomSummaries.map { it.identifier() }.contains(roomId.value) - }.first() + } + .first() } roomId } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 0bc299d020..71e6e730e5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -93,7 +93,7 @@ class RustMatrixAuthenticationService @Inject constructor( client.restoreSession(sessionData.toSession()) createMatrixClient(client) } else { - throw IllegalStateException("No session to restore with id $sessionId") + error("No session to restore with id $sessionId") } }.mapFailure { failure -> failure.mapClientException() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index d2a528074c..397aa1e5b8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -250,6 +250,12 @@ class RustMatrixRoom( } } + override suspend fun canUserRedact(userId: UserId): Result { + return runCatching { + innerRoom.canUserRedact(userId.value) + } + } + override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result { return runCatching { innerRoom.canUserSendState(userId.value, type.map()) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt index 3e65fa2e94..133bf508ce 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt @@ -64,7 +64,8 @@ internal class RustRoomSummaryDataSource( .map { it.toRoomSummaryDataSourceLoadingState() } .onEach { allRoomsLoadingState.value = it - }.launchIn(this) + } + .launchIn(this) launch { // Wait until running, as invites is only available after that diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 796b48ebb3..12e5164307 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -118,7 +118,8 @@ class RustMatrixTimeline( innerRoom.backPaginationStatusFlow() .onEach { postPaginationStatus(it) - }.launchIn(this) + } + .launchIn(this) taskHandleBag += fetchMembers().getOrNull() }.invokeOnCompletion { diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt index 91f0bc1883..3270f6048f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt @@ -27,7 +27,7 @@ import java.util.Date class TimelineEncryptedHistoryPostProcessorTest { - private val defaultLastLoginTimestamp = Date(1689061264L) + private val defaultLastLoginTimestamp = Date(1_689_061_264L) @Test fun `given an unencrypted room, nothing is done`() { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 59f6ed57bd..ee735ae81b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -56,6 +56,7 @@ class FakeMatrixRoom( override val joinedMemberCount: Long = 123L, override val activeMemberCount: Long = 234L, private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(), + canRedact: Boolean = false, ) : MatrixRoom { private var ignoreResult: Result = Result.success(Unit) @@ -66,6 +67,7 @@ class FakeMatrixRoom( private var joinRoomResult = Result.success(Unit) private var inviteUserResult = Result.success(Unit) private var canInviteResult = Result.success(true) + private var canRedactResult = Result.success(canRedact) private val canSendStateResults = mutableMapOf>() private val canSendEventResults = mutableMapOf>() private var sendMediaResult = Result.success(Unit) @@ -207,6 +209,10 @@ class FakeMatrixRoom( return canInviteResult } + override suspend fun canUserRedact(userId: UserId): Result { + return canRedactResult + } + override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result { return canSendStateResults[type] ?: Result.failure(IllegalStateException("No fake answer")) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt index b12f577c36..d541b534b1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt @@ -111,12 +111,12 @@ private fun AvatarActionBottomSheetContent( @Preview @Composable -fun AvatarActionBottomSheetLightPreview() = +internal fun AvatarActionBottomSheetLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun AvatarActionBottomSheetDarkPreview() = +internal fun AvatarActionBottomSheetDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt index 6054aa53af..a42d44b642 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt @@ -103,12 +103,12 @@ private fun MatrixUserHeaderContent( @Preview @Composable -fun MatrixUserHeaderLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = +internal fun MatrixUserHeaderLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreviewLight { ContentToPreview(matrixUser) } @Preview @Composable -fun MatrixUserHeaderDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = +internal fun MatrixUserHeaderDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreviewDark { ContentToPreview(matrixUser) } @Composable diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt index 57901edc03..faa80f82e6 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt @@ -68,12 +68,12 @@ fun MatrixUserHeaderPlaceholder( @Preview @Composable -fun MatrixUserHeaderPlaceholderLightPreview() = +internal fun MatrixUserHeaderPlaceholderLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun MatrixUserHeaderPlaceholderDarkPreview() = +internal fun MatrixUserHeaderPlaceholderDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt index 2b5d2f6800..13fcb40792 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt @@ -85,11 +85,11 @@ fun UnsavedAvatar( @Preview @Composable -fun UnsavedAvatarLightPreview() = ElementPreviewLight { ContentToPreview() } +internal fun UnsavedAvatarLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun UnsavedAvatarDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun UnsavedAvatarDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable private fun ContentToPreview() { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt index 7995672e92..668b963bf1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt @@ -57,14 +57,9 @@ fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): St val roomMembers = roomMembersState.roomMembers() return remember(roomMembersState) { derivedStateOf { - if (roomMembers == null) { - null - } else if (roomMembers.size == 2 && isDirect && isEncrypted) { - roomMembers.find { it.userId != this.sessionId } - } else { - null - } + roomMembers + ?.takeIf { it.size == 2 && isDirect && isEncrypted } + ?.find { it.userId != sessionId } } } } - diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index 005a0ac747..f2a73545bf 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.State import androidx.compose.runtime.produceState import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MessageEventType +import io.element.android.libraries.matrix.api.room.powerlevels.canRedact import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage @Composable @@ -30,3 +31,10 @@ fun MatrixRoom.canSendMessageAsState(type: MessageEventType, updateKey: Long): S } } +@Composable +fun MatrixRoom.canRedactAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canRedact().getOrElse { false } + } +} + diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt index 30f19aa31c..d9dba2a0d3 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt @@ -83,12 +83,12 @@ fun PermissionsView( @Preview @Composable -fun PermissionsViewLightPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = +internal fun PermissionsViewLightPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun PermissionsViewDarkPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = +internal fun PermissionsViewDarkPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt index 734c34b051..96a8b90f06 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt @@ -23,17 +23,15 @@ import io.element.android.libraries.matrix.api.core.SessionId * Data class to hold information about a group of notifications for a room. */ data class RoomEventGroupInfo( - val sessionId: SessionId, - val roomId: RoomId, - val roomDisplayName: String, - val isDirect: Boolean = false -) { + val sessionId: SessionId, + val roomId: RoomId, + val roomDisplayName: String, + val isDirect: Boolean = false, // An event in the list has not yet been display - var hasNewEvent: Boolean = false - + val hasNewEvent: Boolean = false, // true if at least one on the not yet displayed event is noisy - var shouldBing: Boolean = false - var customSound: String? = null - var hasSmartReplyError: Boolean = false - var isUpdated: Boolean = false -} + val shouldBing: Boolean = false, + val customSound: String? = null, + val hasSmartReplyError: Boolean = false, + val isUpdated: Boolean = false, +) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index dba58d165c..29d828d34c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -85,12 +85,11 @@ class RoomGroupMessageCreator @Inject constructor( roomId = roomId, roomDisplayName = roomName, isDirect = !roomIsGroup, - ).also { - it.hasSmartReplyError = smartReplyErrors.isNotEmpty() - it.shouldBing = meta.shouldBing - it.customSound = events.last().soundName - it.isUpdated = events.last().isUpdated - }, + hasSmartReplyError = smartReplyErrors.isNotEmpty(), + shouldBing = meta.shouldBing, + customSound = events.last().soundName, + isUpdated = events.last().isUpdated, + ), threadId = lastKnownRoomEvent.threadId, largeIcon = largeBitmap, lastMessageTimestamp, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt index f252765530..4b262983d4 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt @@ -30,7 +30,7 @@ data class SimpleNotifiableEvent( val type: String?, val timestamp: Long, val soundName: String?, - override var canBeReplaced: Boolean, + override val canBeReplaced: Boolean, override val isRedacted: Boolean = false, override val isUpdated: Boolean = false ) : NotifiableEvent diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt index aa1d0032e0..c3d68e52ac 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt @@ -100,15 +100,15 @@ class DefaultPushHandler @Inject constructor( } val clientSecret = pushData.clientSecret - val userId = if (clientSecret == null) { - // Should not happen. In this case, restore default session - null - } else { - // Get userId from client secret - pushClientSecret.getUserIdFromSecret(clientSecret) - } ?: run { - matrixAuthenticationService.getLatestSessionId() - } + // clientSecret should not be null. If this happens, restore default session + val userId = clientSecret + ?.let { + // Get userId from client secret + pushClientSecret.getUserIdFromSecret(clientSecret) + } + ?: run { + matrixAuthenticationService.getLatestSessionId() + } if (userId == null) { Timber.w("Unable to get a session") diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt index 57f28e72db..b9664ef577 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt @@ -25,7 +25,7 @@ class NotificationIdProviderTest { @Test fun `test notification id provider`() { val sut = NotificationIdProvider() - val offsetForASessionId = 305410 + val offsetForASessionId = 305_410 assertThat(sut.getSummaryNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 0) assertThat(sut.getRoomMessagesNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 1) assertThat(sut.getRoomEventNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 2) diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt index 9dedf9648f..795c8bb1e8 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt @@ -36,7 +36,7 @@ import io.element.android.libraries.pushproviders.api.PushData data class PushDataFirebase( val eventId: String?, val roomId: String?, - var unread: Int?, + val unread: Int?, val clientSecret: String? ) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt index f092d0167c..4485cb2c7f 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt @@ -47,7 +47,7 @@ data class PushDataUnifiedPush( data class PushDataUnifiedPushNotification( @SerialName("event_id") val eventId: String? = null, @SerialName("room_id") val roomId: String? = null, - @SerialName("counts") var counts: PushDataUnifiedPushCounts? = null, + @SerialName("counts") val counts: PushDataUnifiedPushCounts? = null, ) @Serializable diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 580b7670ea..d407924323 100644 --- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -75,6 +75,7 @@ import io.element.android.libraries.designsystem.VectorIcons import io.element.android.libraries.designsystem.modifiers.applyIf import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.text.applyScaleUp import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text @@ -111,12 +112,15 @@ fun TextComposer( ) { AttachmentButton(onClick = onAddAttachment, modifier = Modifier.padding(vertical = 6.dp)) Spacer(modifier = Modifier.width(12.dp)) + val roundCornerSmall = 20.dp.applyScaleUp() + val roundCornerLarge = 28.dp.applyScaleUp() + var lineCount by remember { mutableStateOf(0) } val roundedCornerSize = remember(lineCount, composerMode) { if (lineCount > 1 || composerMode is MessageComposerMode.Special) { - 20.dp + roundCornerSmall } else { - 28.dp + roundCornerLarge } } val roundedCornerSizeState = animateDpAsState( @@ -126,7 +130,7 @@ fun TextComposer( ) ) val roundedCorners = RoundedCornerShape(roundedCornerSizeState.value) - val minHeight = 42.dp + val minHeight = 42.dp.applyScaleUp() val bgColor = ElementTheme.colors.bgSubtleSecondary // Change border color depending on focus var hasFocus by remember { mutableStateOf(false) } @@ -170,7 +174,12 @@ fun TextComposer( singleLine = false, visualTransformation = VisualTransformation.None, shape = roundedCorners, - contentPadding = PaddingValues(top = 10.dp, bottom = 10.dp, start = 12.dp, end = 42.dp), + contentPadding = PaddingValues( + top = 10.dp.applyScaleUp(), + bottom = 10.dp.applyScaleUp(), + start = 12.dp.applyScaleUp(), + end = 42.dp.applyScaleUp(), + ), interactionSource = remember { MutableInteractionSource() }, placeholder = { Text(stringResource(CommonStrings.common_message), style = defaultTypography) @@ -198,7 +207,7 @@ fun TextComposer( canSendMessage = composerCanSendMessage, onSendMessage = onSendMessage, composerMode = composerMode, - modifier = Modifier.padding(end = 6.dp, bottom = 6.dp) + modifier = Modifier.padding(end = 6.dp.applyScaleUp(), bottom = 6.dp.applyScaleUp()) ) } } @@ -258,7 +267,7 @@ private fun EditingModeView( tint = ElementTheme.materialColors.secondary, modifier = Modifier .padding(vertical = 8.dp) - .size(16.dp), + .size(16.dp.applyScaleUp()), ) Text( stringResource(CommonStrings.common_editing), @@ -275,7 +284,7 @@ private fun EditingModeView( tint = ElementTheme.materialColors.secondary, modifier = Modifier .padding(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 12.dp) - .size(16.dp) + .size(16.dp.applyScaleUp()) .clickable( enabled = true, onClick = onResetComposerMode, @@ -338,7 +347,7 @@ private fun ReplyToModeView( tint = MaterialTheme.colorScheme.secondary, modifier = Modifier .padding(end = 4.dp, top = 4.dp, start = 16.dp, bottom = 16.dp) - .size(16.dp) + .size(16.dp.applyScaleUp()) .clickable( enabled = true, onClick = onResetComposerMode, @@ -356,13 +365,13 @@ private fun AttachmentButton( ) { Surface( modifier - .size(30.dp) + .size(30.dp.applyScaleUp()) .clickable(onClick = onClick), shape = CircleShape, color = ElementTheme.colors.iconPrimary ) { Image( - modifier = Modifier.size(12.5f.dp), + modifier = Modifier.size(12.5f.dp.applyScaleUp()), painter = painterResource(R.drawable.ic_add_attachment), contentDescription = stringResource(R.string.rich_text_editor_a11y_add_attachment), contentScale = ContentScale.Inside, @@ -386,10 +395,10 @@ private fun BoxScope.SendButton( modifier = modifier .clip(CircleShape) .background(if (canSendMessage) ElementTheme.colors.iconAccentTertiary else Color.Transparent) - .size(30.dp) + .size(30.dp.applyScaleUp()) .align(Alignment.BottomEnd) .applyIf(composerMode !is MessageComposerMode.Edit, ifTrue = { - padding(start = 1.dp) // Center the arrow in the circle + padding(start = 1.dp.applyScaleUp()) // Center the arrow in the circle }) .clickable( enabled = canSendMessage, @@ -409,7 +418,7 @@ private fun BoxScope.SendButton( else -> stringResource(CommonStrings.action_send) } Icon( - modifier = Modifier.size(16.dp), + modifier = Modifier.size(16.dp.applyScaleUp()), resourceId = iconId, contentDescription = contentDescription, // Exception here, we use Color.White instead of ElementTheme.colors.iconOnSolidPrimary @@ -420,7 +429,7 @@ private fun BoxScope.SendButton( @DayNightPreviews @Composable -fun TextComposerSimplePreview() = ElementPreview { +internal fun TextComposerSimplePreview() = ElementPreview { Column { TextComposer( onSendMessage = {}, @@ -451,7 +460,7 @@ fun TextComposerSimplePreview() = ElementPreview { @DayNightPreviews @Composable -fun TextComposerEditPreview() = ElementPreview { +internal fun TextComposerEditPreview() = ElementPreview { TextComposer( onSendMessage = {}, onComposerTextChange = {}, @@ -464,7 +473,7 @@ fun TextComposerEditPreview() = ElementPreview { @DayNightPreviews @Composable -fun TextComposerReplyPreview() = ElementPreview { +internal fun TextComposerReplyPreview() = ElementPreview { Column { TextComposer( onSendMessage = {}, diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt index d211869f71..3d359594e1 100644 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt +++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt @@ -91,7 +91,7 @@ internal val materialColorSchemeDark = darkColorScheme( @Preview @Composable -fun ColorsSchemePreviewLight() = ColorsSchemePreview( +internal fun ColorsSchemePreviewLight() = ColorsSchemePreview( Color.Black, Color.White, materialColorSchemeLight, @@ -99,7 +99,7 @@ fun ColorsSchemePreviewLight() = ColorsSchemePreview( @Preview @Composable -fun ColorsSchemePreviewDark() = ColorsSchemePreview( +internal fun ColorsSchemePreviewDark() = ColorsSchemePreview( Color.White, Color.Black, materialColorSchemeDark, diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 7870560dd4..500f0a57f7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da4188dc606f0735fd4093acad34897c866d8c4d20b3e0ec0618685f7302cbf5 -size 9288 +oid sha256:3f18e74bfbebd69109f36a7154b20d3ee61071bff0d8ebf28f4b3b35c58e2938 +size 9256 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 7edd6f9ee7..17ed1eee99 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e27b1ea7bfa4eb97af3c2d433fbe3f5ca21458e4458d91b39c9c2bb3d7b02abc -size 11306 +oid sha256:eea203b9527f7c3dc3692e7db3e9d5cee20ca08fddf04977c948933dc2784022 +size 11274 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index c87156e8c6..3bbf79781a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bbeb21faf320226f42aad179b955952ad8e4b5d5645c45bfbbc02166baf0662 -size 9641 +oid sha256:b5c69ae7d27eb7024e9a2f9c7100b308562ef2fc702e28604098180cb02a3813 +size 9645 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 6391f1dec0..1b135effbf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:499e17b52d57f8b0d8984e32457b3922c0647be5b835c02a01ed927fe004ec4d -size 11610 +oid sha256:62149a4d9b0dd62ddb56e1cdb24fe401f189fda05d17dd16f8c0bc511eab6561 +size 11616 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 482ab3b775..3dc03be3ae 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d368f7cc84b5a8850577fac658aaeb89a1c83a2890b7fd393577c9cde919069 -size 53689 +oid sha256:613d55031bc30060bdd84596fb78de043ead483ac59fd42e48b15f0e56947a36 +size 53660 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index ad548c2fbe..26d1cd82c3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89520fd2999582229ace8fd9304644fc74ce01230a4024b90b47ce1cd61eb564 -size 55678 +oid sha256:632dfa6edc34b7283654e43c3b7d9eb0334507644e50bc5fc7bdf8b5c45ed874 +size 55686 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png index fd00dfae33..41503c31be 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e0c73b8d86c064ce46ade6477cc91803d543199657e597a15a7e21bdacab7be -size 6541 +oid sha256:774ece5436ab5a025a6a7134120069922276329bf87408a90263bb8c425a0568 +size 6510 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png index 820b688066..b41bc75694 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3af4d7ce438da56851aa3041138558d8e57ac181775476b983646039a1cb62ae -size 6602 +oid sha256:55aff072541c752abd0a8fe48c73b38b42ea47ab4779a2a3ae37a449f88ffd24 +size 6606 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 6ef66d5ddf..a613701e1a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8aa48154bf2cf3a6ec6da96e24a86c6a3f1e6eb11865c2301a4f2638e471bd34 -size 37408 +oid sha256:9c6477d7476baccee037e491930d99f7ea00e313d1285fdd882be3de99f705b0 +size 37376 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 75268923f9..de27672e6f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8f155381ccc3f8833b58e076e797034dfa528bd55bafd982c34fb997e4db491 -size 39830 +oid sha256:5b589817850bc30e336e9d5e443b2153133991aa5ea919838bddef26d2b1df9e +size 39840 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..60a3b8f39e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:373fcfdea163cd59d1b929adb1abf7163c0e1aaa203033292931fd22cd5c60d6 +size 22378 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9a02d72493 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1e171c6295cd93af0fc0533b8c3099c75d5343132477709e231848c0c51b35f +size 24366 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9600daf12b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45485288d1cd0552c371aa868600e1fd8c30232ea2d9cb26420ec36c539cdb6f +size 28228 diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 0123535b48..14193a1c70 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -1,6 +1,21 @@ # Default rules: https://github.com/detekt/detekt/blob/main/detekt-core/src/main/resources/default-detekt-config.yml style: + AlsoCouldBeApply: + active: true + CascadingCallWrapping: + active: true + includeElvis: true + DataClassShouldBeImmutable: + active: true + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: true + ExplicitCollectionElementAccessMethod: + active: true + ExplicitItLambdaParameter: + active: true MaxLineLength: # Default is 120 maxLineLength: 160 @@ -9,7 +24,7 @@ style: ReturnCount: active: false UnnecessaryAbstractClass: - active: false + active: true FunctionOnlyReturningConstant: active: false UnusedPrivateMember: @@ -32,23 +47,94 @@ style: ThrowsCount: active: false LoopWithTooManyJumpStatements: - active: false + active: true SerialVersionUIDInSerializableClass: active: false ProtectedMemberInFinalClass: - active: false + active: true UseCheckOrError: + active: true + OptionalUnit: + active: true + PreferToOverPairSyntax: + active: true + RedundantExplicitType: + active: true + TrailingWhitespace: + active: true + TrimMultilineRawString: + active: true + trimmingMethods: + - 'trimIndent' + - 'trimMargin' + UnderscoresInNumericLiterals: + active: true + acceptableLength: 4 + allowNonStandardGrouping: false + UnnecessaryAnnotationUseSiteTarget: + active: true + UnnecessaryBackticks: + active: true + UnnecessaryBracesAroundTrailingLambda: + active: true + UseDataClass: + active: true + allowVars: false + UseEmptyCounterpart: + active: true + UseIfEmptyOrIfBlank: + active: true + UseLet: + active: true + UseSumOfInsteadOfFlatMapSize: + active: true + +coroutines: + GlobalCoroutineUsage: + # Keep false for now. active: false + SuspendFunSwallowedCancellation: + active: true + SuspendFunWithCoroutineScopeReceiver: + active: true empty-blocks: EmptyFunctionBlock: active: false EmptySecondaryConstructor: - active: false + active: true potential-bugs: ImplicitDefaultLocale: - active: false + active: true + CastNullableToNonNullableType: + active: true + CastToNullableType: + active: true + Deprecation: + active: true + DontDowncastCollectionTypes: + active: true + ElseCaseInsteadOfExhaustiveWhen: + active: true + ExitOutsideMain: + active: true + ImplicitUnitReturnType: + active: true + allowExplicitReturnType: false + MissingPackageDeclaration: + active: true + excludes: ['**/*.kts'] + NullCheckOnMutableProperty: + active: true + NullableToStringCall: + active: true + PropertyUsedBeforeDeclaration: + active: true + UnconditionalJumpStatementInLoop: + active: true + UnnecessaryNotNullCheck: + active: true exceptions: TooGenericExceptionCaught: @@ -56,11 +142,13 @@ exceptions: SwallowedException: active: false ThrowingExceptionsWithoutMessageOrCause: - active: false + active: true TooGenericExceptionThrown: - active: false + active: true InstanceOfCheckForException: - active: false + active: true + ObjectExtendsThrowable: + active: true complexity: TooManyFunctions: @@ -74,9 +162,9 @@ complexity: NestedBlockDepth: active: false ComplexCondition: - active: false + active: true LargeClass: - active: false + active: true naming: VariableNaming: @@ -86,10 +174,20 @@ naming: FunctionNaming: active: true ignoreAnnotated: ['Composable'] + LambdaParameterNaming: + active: true + NonBooleanPropertyPrefixedWithIs: + active: true + VariableMaxLength: + active: true performance: SpreadOperator: active: false + CouldBeSequence: + active: true + UnnecessaryPartOfBinaryExpression: + active: true # Note: all rules for `comments` are disabled by default, but I put them here to be aware of their existence comments: @@ -149,7 +247,7 @@ Compose: PreviewNaming: active: true PreviewPublic: - active: false + active: true # You can optionally disable that only previews with @PreviewParameter are flagged previewPublicOnlyIfParams: false RememberMissing: