Konsist: add test to ensure filename match top level Composable View, and fix existing issue

This commit is contained in:
Benoit Marty 2023-10-10 14:37:01 +02:00
parent 675f77bfa4
commit 308a0c0f57
103 changed files with 1137 additions and 760 deletions

View file

@ -34,7 +34,7 @@ import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
import com.bumble.appyx.core.plugin.NodeReadyObserver import com.bumble.appyx.core.plugin.NodeReadyObserver
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.designsystem.utils.LocalSnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher
import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.theme.ElementTheme
import io.element.android.x.di.AppBindings import io.element.android.x.di.AppBindings
import io.element.android.x.intent.SafeUriHandler import io.element.android.x.intent.SafeUriHandler

View file

@ -18,7 +18,7 @@ package io.element.android.x.di
import com.squareup.anvil.annotations.ContributesTo import com.squareup.anvil.annotations.ContributesTo
import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.tracing.TracingService import io.element.android.libraries.matrix.api.tracing.TracingService

View file

@ -28,7 +28,7 @@ import io.element.android.features.messages.impl.timeline.components.customreact
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.DefaultPreferences import io.element.android.libraries.di.DefaultPreferences

View file

@ -16,9 +16,15 @@
package io.element.android.app package io.element.android.app
import androidx.compose.runtime.Composable
import com.lemonappdev.konsist.api.KoModifier
import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.Konsist
import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutModifier
import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
import com.lemonappdev.konsist.api.ext.list.withAllParentsOf import com.lemonappdev.konsist.api.ext.list.withAllParentsOf
import com.lemonappdev.konsist.api.ext.list.withTopLevel
import com.lemonappdev.konsist.api.ext.list.withoutName
import com.lemonappdev.konsist.api.ext.list.withoutNameEndingWith
import com.lemonappdev.konsist.api.verify.assertTrue import com.lemonappdev.konsist.api.verify.assertTrue
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -46,4 +52,36 @@ class KonsistTest {
it.hasNameEndingWith("DarkPreview").not() it.hasNameEndingWith("DarkPreview").not()
} }
} }
@Test
fun `top level function with '@Composable' annotation starting with a upper case should be placed in a file with the same name`() {
Konsist
.scopeFromProject()
.functions()
.withTopLevel()
.withoutModifier(KoModifier.PRIVATE)
.withoutNameEndingWith("Preview")
.withAllAnnotationsOf(Composable::class)
.withoutName(
// Add some exceptions...
"OutlinedButton",
"TextButton",
"SimpleAlertDialogContent",
)
.assertTrue(
additionalMessage =
"""
Please check the filename. It should match the top level Composable function. If the filename is correct:
- consider making the Composable private or moving it to its own file
- at last resort, you can add an exception in the Konsist test
""".trimIndent()
) {
if (it.name.first().isLowerCase()) {
true
} else {
val fileName = it.containingFile.name.removeSuffix(".kt")
fileName == it.name
}
}
}
} }

View file

@ -16,8 +16,8 @@
package io.element.android.appnav package io.element.android.appnav
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.VerificationFlowState import io.element.android.libraries.matrix.api.verification.VerificationFlowState

View file

@ -58,7 +58,7 @@ import io.element.android.libraries.architecture.animation.rememberDefaultTransi
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.architecture.waitForChildAttached
import io.element.android.libraries.deeplink.DeeplinkData import io.element.android.libraries.deeplink.DeeplinkData
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.MAIN_SPACE import io.element.android.libraries.matrix.api.core.MAIN_SPACE

View file

@ -84,7 +84,7 @@ fun AddPeopleView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun AddPeopleViewTopBar( private fun AddPeopleViewTopBar(
hasSelectedUsers: Boolean, hasSelectedUsers: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {}, onBackPressed: () -> Unit = {},

View file

@ -181,7 +181,7 @@ fun ConfigureRoomView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ConfigureRoomToolbar( private fun ConfigureRoomToolbar(
isNextActionEnabled: Boolean, isNextActionEnabled: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {}, onBackPressed: () -> Unit = {},
@ -207,7 +207,7 @@ fun ConfigureRoomToolbar(
} }
@Composable @Composable
fun RoomNameWithAvatar( private fun RoomNameWithAvatar(
avatarUri: Uri?, avatarUri: Uri?,
roomName: String, roomName: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -235,7 +235,7 @@ fun RoomNameWithAvatar(
} }
@Composable @Composable
fun RoomTopic( private fun RoomTopic(
topic: String, topic: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onTopicChanged: (String) -> Unit = {}, onTopicChanged: (String) -> Unit = {},
@ -254,7 +254,7 @@ fun RoomTopic(
} }
@Composable @Composable
fun RoomPrivacyOptions( private fun RoomPrivacyOptions(
selected: RoomPrivacy?, selected: RoomPrivacy?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onOptionSelected: (RoomPrivacyItem) -> Unit = {}, onOptionSelected: (RoomPrivacyItem) -> Unit = {},

View file

@ -126,7 +126,7 @@ fun CreateRoomRootView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun CreateRoomRootViewTopBar( private fun CreateRoomRootViewTopBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClosePressed: () -> Unit = {}, onClosePressed: () -> Unit = {},
) { ) {
@ -148,7 +148,7 @@ fun CreateRoomRootViewTopBar(
} }
@Composable @Composable
fun CreateRoomActionButtonsList( private fun CreateRoomActionButtonsList(
state: CreateRoomRootState, state: CreateRoomRootState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onNewRoomClicked: () -> Unit = {}, onNewRoomClicked: () -> Unit = {},
@ -169,7 +169,7 @@ fun CreateRoomActionButtonsList(
} }
@Composable @Composable
fun CreateRoomActionButton( private fun CreateRoomActionButton(
@DrawableRes iconRes: Int, @DrawableRes iconRes: Int,
text: String, text: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

View file

@ -111,7 +111,7 @@ fun InviteListView(
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable @Composable
fun InviteListContent( private fun InviteListContent(
state: InviteListState, state: InviteListState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackClicked: () -> Unit = {}, onBackClicked: () -> Unit = {},

View file

@ -77,7 +77,7 @@ internal fun InviteSummaryRow(
} }
@Composable @Composable
internal fun DefaultInviteSummaryRow( private fun DefaultInviteSummaryRow(
invite: InviteListInviteSummary, invite: InviteListInviteSummary,
onAcceptClicked: () -> Unit = {}, onAcceptClicked: () -> Unit = {},
onDeclineClicked: () -> Unit = {}, onDeclineClicked: () -> Unit = {},

View file

@ -167,7 +167,7 @@ fun LoginPasswordView(
@OptIn(ExperimentalComposeUiApi::class) @OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
internal fun LoginForm( private fun LoginForm(
state: LoginPasswordState, state: LoginPasswordState,
isLoading: Boolean, isLoading: Boolean,
onSubmit: () -> Unit, onSubmit: () -> Unit,
@ -272,7 +272,7 @@ internal fun LoginForm(
} }
@Composable @Composable
internal fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) { private fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) {
ErrorDialog( ErrorDialog(
title = stringResource(id = CommonStrings.dialog_title_error), title = stringResource(id = CommonStrings.dialog_title_error),
content = stringResource(loginError(error)), content = stringResource(loginError(error)),

View file

@ -74,7 +74,7 @@ fun LogoutPreferenceView(
} }
@Composable @Composable
fun LogoutPreferenceContent( private fun LogoutPreferenceContent(
onClick: () -> Unit = {}, onClick: () -> Unit = {},
) { ) {
PreferenceText( PreferenceText(

View file

@ -64,9 +64,9 @@ import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState

View file

@ -25,7 +25,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@Immutable @Immutable

View file

@ -79,8 +79,8 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.theme.ElementTheme

View file

@ -336,7 +336,7 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
private val emojiRippleRadius = 24.dp private val emojiRippleRadius = 24.dp
@Composable @Composable
internal fun EmojiReactionsRow( private fun EmojiReactionsRow(
highlightedEmojis: ImmutableList<String>, highlightedEmojis: ImmutableList<String>,
onEmojiReactionClicked: (String) -> Unit, onEmojiReactionClicked: (String) -> Unit,
onCustomReactionClicked: () -> Unit, onCustomReactionClicked: () -> Unit,

View file

@ -204,7 +204,7 @@ fun ForwardMessagesView(
} }
@Composable @Composable
internal fun SelectedRooms( private fun SelectedRooms(
selectedRooms: ImmutableList<RoomSummaryDetails>, selectedRooms: ImmutableList<RoomSummaryDetails>,
onRoomRemoved: (RoomSummaryDetails) -> Unit, onRoomRemoved: (RoomSummaryDetails) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -221,7 +221,7 @@ internal fun SelectedRooms(
} }
@Composable @Composable
internal fun RoomSummaryView( private fun RoomSummaryView(
summary: RoomSummaryDetails, summary: RoomSummaryDetails,
isSelected: Boolean, isSelected: Boolean,
onSelection: (RoomSummaryDetails) -> Unit, onSelection: (RoomSummaryDetails) -> Unit,

View file

@ -142,7 +142,7 @@ private fun MediaImageView(
@UnstableApi @UnstableApi
@Composable @Composable
fun MediaVideoView( private fun MediaVideoView(
localMediaViewState: LocalMediaViewState, localMediaViewState: LocalMediaViewState,
localMedia: LocalMedia?, localMedia: LocalMedia?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -196,7 +196,7 @@ fun MediaVideoView(
} }
@Composable @Composable
fun MediaPDFView( private fun MediaPDFView(
localMediaViewState: LocalMediaViewState, localMediaViewState: LocalMediaViewState,
localMedia: LocalMedia?, localMedia: LocalMedia?,
zoomableState: ZoomableState, zoomableState: ZoomableState,
@ -211,7 +211,7 @@ fun MediaPDFView(
} }
@Composable @Composable
fun MediaFileView( private fun MediaFileView(
localMediaViewState: LocalMediaViewState, localMediaViewState: LocalMediaViewState,
uri: Uri?, uri: Uri?,
info: MediaInfo?, info: MediaInfo?,

View file

@ -34,9 +34,9 @@ import io.element.android.features.messages.impl.media.local.LocalMediaActions
import io.element.android.features.messages.impl.media.local.LocalMediaFactory import io.element.android.features.messages.impl.media.local.LocalMediaFactory
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.matrix.api.media.MediaFile
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings

View file

@ -19,7 +19,7 @@ package io.element.android.features.messages.impl.media.viewer
import io.element.android.features.messages.impl.media.local.LocalMedia import io.element.android.features.messages.impl.media.local.LocalMedia
import io.element.android.features.messages.impl.media.local.MediaInfo import io.element.android.features.messages.impl.media.local.MediaInfo
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
data class MediaViewerState( data class MediaViewerState(

View file

@ -62,8 +62,8 @@ import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.matrix.ui.media.MediaRequestData
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings

View file

@ -95,7 +95,7 @@ internal fun AttachmentsBottomSheet(
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
internal fun AttachmentSourcePickerMenu( private fun AttachmentSourcePickerMenu(
state: MessageComposerState, state: MessageComposerState,
onSendLocationClicked: () -> Unit, onSendLocationClicked: () -> Unit,
onCreatePollClicked: () -> Unit, onCreatePollClicked: () -> Unit,

View file

@ -35,8 +35,8 @@ import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError
import io.element.android.features.messages.impl.media.local.LocalMediaFactory import io.element.android.features.messages.impl.media.local.LocalMediaFactory
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService

View file

@ -30,8 +30,8 @@ import dagger.assisted.AssistedInject
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.architecture.runUpdatingState
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom

View file

@ -155,7 +155,7 @@ fun TimelineView(
} }
@Composable @Composable
fun TimelineItemRow( private fun TimelineItemRow(
timelineItem: TimelineItem, timelineItem: TimelineItem,
highlightedItem: String?, highlightedItem: String?,
userHasPermissionToSendMessage: Boolean, userHasPermissionToSendMessage: Boolean,

View file

@ -213,7 +213,7 @@ fun TimelineItemEventRow(
* @param content the content to display. * @param content the content to display.
*/ */
@Composable @Composable
fun SwipeSensitivity( private fun SwipeSensitivity(
sensitivityFactor: Float, sensitivityFactor: Float,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
@ -306,7 +306,7 @@ private fun TimelineItemEventRowContent(
// Reactions // Reactions
if (event.reactionsState.reactions.isNotEmpty()) { if (event.reactionsState.reactions.isNotEmpty()) {
TimelineItemReactions( TimelineItemReactionsView(
reactionsState = event.reactionsState, reactionsState = event.reactionsState,
isOutgoing = event.isMine, isOutgoing = event.isMine,
onReactionClicked = onReactionClicked, onReactionClicked = onReactionClicked,
@ -315,7 +315,7 @@ private fun TimelineItemEventRowContent(
modifier = Modifier modifier = Modifier
.constrainAs(reactions) { .constrainAs(reactions) {
top.linkTo(message.bottom, margin = (-4).dp) top.linkTo(message.bottom, margin = (-4).dp)
this.linkStartOrEnd(event) linkStartOrEnd(event)
} }
.zIndex(1f) .zIndex(1f)
.padding(start = if (event.isMine) 16.dp else 36.dp, end = 16.dp) .padding(start = if (event.isMine) 16.dp else 36.dp, end = 16.dp)

View file

@ -37,7 +37,7 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@Composable @Composable
fun TimelineItemReactions( fun TimelineItemReactionsView(
reactionsState: TimelineItemReactions, reactionsState: TimelineItemReactions,
isOutgoing: Boolean, isOutgoing: Boolean,
onReactionClicked: (emoji: String) -> Unit, onReactionClicked: (emoji: String) -> Unit,
@ -46,16 +46,16 @@ fun TimelineItemReactions(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
var expanded: Boolean by rememberSaveable { mutableStateOf(false) } var expanded: Boolean by rememberSaveable { mutableStateOf(false) }
TimelineItemReactionsView( TimelineItemReactionsView(
modifier = modifier, modifier = modifier,
reactions = reactionsState.reactions, reactions = reactionsState.reactions,
expanded = expanded, expanded = expanded,
isOutgoing = isOutgoing, isOutgoing = isOutgoing,
onReactionClick = onReactionClicked, onReactionClick = onReactionClicked,
onReactionLongClick = onReactionLongClicked, onReactionLongClick = onReactionLongClicked,
onMoreReactionsClick = onMoreReactionsClicked, onMoreReactionsClick = onMoreReactionsClicked,
onToggleExpandClick = { expanded = !expanded }, onToggleExpandClick = { expanded = !expanded },
) )
} }
@Composable @Composable
@ -153,7 +153,7 @@ private fun ContentToPreview(
reactions: ImmutableList<AggregatedReaction>, reactions: ImmutableList<AggregatedReaction>,
isOutgoing: Boolean = false isOutgoing: Boolean = false
) { ) {
TimelineItemReactions( TimelineItemReactionsView(
reactionsState = TimelineItemReactions( reactionsState = TimelineItemReactions(
reactions reactions
), ),

View file

@ -160,7 +160,7 @@ private fun SheetContent(
} }
@Composable @Composable
fun AggregatedReactionButton( private fun AggregatedReactionButton(
reaction: AggregatedReaction, reaction: AggregatedReaction,
isHighlighted: Boolean, isHighlighted: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
@ -215,7 +215,7 @@ fun AggregatedReactionButton(
} }
@Composable @Composable
fun SenderRow( private fun SenderRow(
avatarData: AvatarData, avatarData: AvatarData,
name: String, name: String,
userId: String, userId: String,

View file

@ -72,7 +72,7 @@ internal fun RetrySendMessageMenu(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
internal fun RetrySendMessageMenuBottomSheet( private fun RetrySendMessageMenuBottomSheet(
isVisible: Boolean, isVisible: Boolean,
onRetry: () -> Unit, onRetry: () -> Unit,
onRemoveFailed: () -> Unit, onRemoveFailed: () -> Unit,

View file

@ -51,7 +51,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.mimetype.MimeTypes 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.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore

View file

@ -30,7 +30,7 @@ import io.element.android.features.messages.impl.media.viewer.MediaViewerPresent
import io.element.android.features.messages.media.FakeLocalMediaActions import io.element.android.features.messages.media.FakeLocalMediaActions
import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.features.messages.media.FakeLocalMediaFactory
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.matrix.test.media.FakeMediaLoader import io.element.android.libraries.matrix.test.media.FakeMediaLoader
import io.element.android.libraries.matrix.test.media.aMediaSource import io.element.android.libraries.matrix.test.media.aMediaSource
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule

View file

@ -23,7 +23,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.messages.impl.report.ReportMessageEvents import io.element.android.features.messages.impl.report.ReportMessageEvents
import io.element.android.features.messages.impl.report.ReportMessagePresenter import io.element.android.features.messages.impl.report.ReportMessagePresenter
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID

View file

@ -33,7 +33,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.features.messages.media.FakeLocalMediaFactory
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService

View file

@ -0,0 +1,79 @@
/*
* 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.features.networkmonitor.api.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.statusBars
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/**
* A view that displays a connectivity indicator when the device is offline, passing the padding
* needed to make sure the status bar is not overlapped to its content views.
*/
@Composable
fun ConnectivityIndicatorContainer(
isOnline: Boolean,
modifier: Modifier = Modifier,
content: @Composable (topPadding: Dp) -> Unit = {},
) {
val isIndicatorVisible = remember { MutableTransitionState(!isOnline) }.apply { targetState = !isOnline }
val statusBarTopPadding = if (LocalInspectionMode.current) {
// Needed to get valid UI previews
24.dp
} else {
WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + 6.dp
}
val target = remember(isIndicatorVisible.targetState, statusBarTopPadding) {
if (!isIndicatorVisible.targetState) 0.dp else statusBarTopPadding
}
val animationStateOffset by animateDpAsState(
targetValue = target,
animationSpec = spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = 1.dp,
),
label = "insets-animation",
)
content(animationStateOffset)
// Display the network indicator with an animation
AnimatedVisibility(
visibleState = isIndicatorVisible,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
Indicator(modifier)
}
}

View file

@ -18,44 +18,17 @@ package io.element.android.features.networkmonitor.api.ui
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.expandVertically import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
/** /**
* A view that displays a connectivity indicator when the device is offline, adding a default * A view that displays a connectivity indicator when the device is offline, adding a default
@ -88,75 +61,6 @@ fun ConnectivityIndicatorView(
} }
} }
/**
* A view that displays a connectivity indicator when the device is offline, passing the padding
* needed to make sure the status bar is not overlapped to its content views.
*/
@Composable
fun ConnectivityIndicatorContainer(
isOnline: Boolean,
modifier: Modifier = Modifier,
content: @Composable (topPadding: Dp) -> Unit,
) {
val isIndicatorVisible = remember { MutableTransitionState(!isOnline) }.apply { targetState = !isOnline }
val statusBarTopPadding = if (LocalInspectionMode.current) {
// Needed to get valid UI previews
24.dp
} else {
WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + 6.dp
}
val target = remember(isIndicatorVisible.targetState, statusBarTopPadding) {
if (!isIndicatorVisible.targetState) 0.dp else statusBarTopPadding
}
val animationStateOffset by animateDpAsState(
targetValue = target,
animationSpec = spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = 1.dp,
),
label = "insets-animation",
)
content(animationStateOffset)
// Display the network indicator with an animation
AnimatedVisibility(
visibleState = isIndicatorVisible,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
Indicator(modifier)
}
}
@Composable
private fun Indicator(modifier: Modifier = Modifier) {
Row(
modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.secondaryContainer)
.statusBarsPadding()
.padding(vertical = 6.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
val tint = MaterialTheme.colorScheme.primary
Icon(
resourceId = CommonDrawables.ic_compound_offline,
contentDescription = null,
tint = tint,
modifier = Modifier.size(16.sp.toDp()),
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(CommonStrings.common_offline),
style = ElementTheme.typography.fontBodyMdMedium,
color = tint,
)
}
}
@Composable @Composable
private fun StatusBarPaddingSpacer(modifier: Modifier = Modifier) { private fun StatusBarPaddingSpacer(modifier: Modifier = Modifier) {
Spacer(modifier = modifier.statusBarsPadding()) Spacer(modifier = modifier.statusBarsPadding())

View file

@ -0,0 +1,69 @@
/*
* 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.features.networkmonitor.api.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
internal fun Indicator(
modifier: Modifier = Modifier,
) {
Row(
modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.secondaryContainer)
.statusBarsPadding()
.padding(vertical = 6.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
val tint = MaterialTheme.colorScheme.primary
Icon(
resourceId = CommonDrawables.ic_compound_offline,
contentDescription = null,
tint = tint,
modifier = Modifier.size(16.sp.toDp()),
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(CommonStrings.common_offline),
style = ElementTheme.typography.fontBodyMdMedium,
color = tint,
)
}
}

View file

@ -21,7 +21,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -33,7 +33,7 @@ fun AboutView(
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
PreferenceView( PreferencePage(
modifier = modifier, modifier = modifier,
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_about) title = stringResource(id = CommonStrings.common_about)

View file

@ -22,7 +22,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.R
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -33,7 +33,7 @@ fun AdvancedSettingsView(
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
PreferenceView( PreferencePage(
modifier = modifier, modifier = modifier,
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_advanced_settings) title = stringResource(id = CommonStrings.common_advanced_settings)

View file

@ -21,7 +21,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesView import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesView
import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -32,7 +32,7 @@ fun AnalyticsSettingsView(
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
PreferenceView( PreferencePage(
modifier = modifier, modifier = modifier,
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_analytics) title = stringResource(id = CommonStrings.common_analytics)

View file

@ -23,7 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesView import io.element.android.features.rageshake.api.preferences.RageshakePreferencesView
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.featureflag.ui.FeatureListView import io.element.android.libraries.featureflag.ui.FeatureListView
@ -38,7 +38,7 @@ fun DeveloperSettingsView(
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
PreferenceView( PreferencePage(
modifier = modifier, modifier = modifier,
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_developer_options) title = stringResource(id = CommonStrings.common_developer_options)
@ -85,7 +85,7 @@ fun DeveloperSettingsView(
} }
@Composable @Composable
fun FeatureListContent( private fun FeatureListContent(
state: DeveloperSettingsState, state: DeveloperSettingsState,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {

View file

@ -139,7 +139,7 @@ fun ConfigureTracingView(
} }
@Composable @Composable
fun CrateListContent( private fun CrateListContent(
state: ConfigureTracingState, state: ConfigureTracingState,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
@ -178,7 +178,7 @@ private fun TargetAndLogLevelListView(
} }
@Composable @Composable
fun TargetAndLogLevelView( private fun TargetAndLogLevelView(
target: Target, target: Target,
logLevel: LogLevel, logLevel: LogLevel,
onLogLevelChange: (LogLevel) -> Unit, onLogLevelChange: (LogLevel) -> Unit,
@ -197,7 +197,7 @@ fun TargetAndLogLevelView(
} }
@Composable @Composable
fun LogLevelDropdownMenu( private fun LogLevelDropdownMenu(
logLevel: LogLevel, logLevel: LogLevel,
onLogLevelChange: (LogLevel) -> Unit, onLogLevelChange: (LogLevel) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

View file

@ -38,7 +38,7 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Button
@ -67,7 +67,7 @@ fun NotificationSettingsView(
else -> Unit else -> Unit
} }
} }
PreferenceView( PreferencePage(
modifier = modifier, modifier = modifier,
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.screen_notification_settings_title) title = stringResource(id = CommonStrings.screen_notification_settings_title)
@ -79,7 +79,7 @@ fun NotificationSettingsView(
onContinueClicked = { state.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) }, onContinueClicked = { state.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) },
onDismissError = { state.eventSink(NotificationSettingsEvents.ClearConfigurationMismatchError) }, onDismissError = { state.eventSink(NotificationSettingsEvents.ClearConfigurationMismatchError) },
) )
NotificationSettingsState.MatrixSettings.Uninitialized -> return@PreferenceView NotificationSettingsState.MatrixSettings.Uninitialized -> return@PreferencePage
is NotificationSettingsState.MatrixSettings.Valid -> NotificationSettingsContentView( is NotificationSettingsState.MatrixSettings.Valid -> NotificationSettingsContentView(
matrixSettings = state.matrixSettings, matrixSettings = state.matrixSettings,
systemSettings = state.appSettings, systemSettings = state.appSettings,

View file

@ -22,7 +22,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -42,7 +42,7 @@ fun EditDefaultNotificationSettingView(
} else { } else {
CommonStrings.screen_notification_settings_group_chats CommonStrings.screen_notification_settings_group_chats
} }
PreferenceView( PreferencePage(
modifier = modifier, modifier = modifier,
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
title = stringResource(id = title) title = stringResource(id = title)

View file

@ -27,8 +27,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.logout.api.LogoutPreferencePresenter import io.element.android.features.logout.api.LogoutPreferencePresenter
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient

View file

@ -17,7 +17,7 @@
package io.element.android.features.preferences.impl.root package io.element.android.features.preferences.impl.root
import io.element.android.features.logout.api.LogoutPreferenceState import io.element.android.features.logout.api.LogoutPreferenceState
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
data class PreferencesRootState( data class PreferencesRootState(

View file

@ -17,7 +17,7 @@
package io.element.android.features.preferences.impl.root package io.element.android.features.preferences.impl.root
import io.element.android.features.logout.api.aLogoutPreferenceState import io.element.android.features.logout.api.aLogoutPreferenceState
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
fun aPreferencesRootState() = PreferencesRootState( fun aPreferencesRootState() = PreferencesRootState(

View file

@ -31,15 +31,15 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.logout.api.LogoutPreferenceView import io.element.android.features.logout.api.LogoutPreferenceView
import io.element.android.features.preferences.impl.user.UserPreferences import io.element.android.features.preferences.impl.user.UserPreferences
import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.MatrixUserProvider import io.element.android.libraries.matrix.ui.components.MatrixUserProvider
import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.theme.ElementTheme
@ -64,7 +64,7 @@ fun PreferencesRootView(
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage) val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
// Include pref from other modules // Include pref from other modules
PreferenceView( PreferencePage(
modifier = modifier, modifier = modifier,
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_settings), title = stringResource(id = CommonStrings.common_settings),
@ -151,7 +151,7 @@ fun PreferencesRootView(
} }
@Composable @Composable
fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) { private fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) {
PreferenceText( PreferenceText(
title = stringResource(id = CommonStrings.common_developer_options), title = stringResource(id = CommonStrings.common_developer_options),
iconResourceId = CommonDrawables.ic_developer_mode, iconResourceId = CommonDrawables.ic_developer_mode,

View file

@ -23,7 +23,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_AVATAR_URL

View file

@ -49,7 +49,7 @@ fun CrashDetectionView(
} }
@Composable @Composable
fun CrashDetectionContent( private fun CrashDetectionContent(
onNoClicked: () -> Unit = { }, onNoClicked: () -> Unit = { },
onYesClicked: () -> Unit = { }, onYesClicked: () -> Unit = { },
onDismiss: () -> Unit = { }, onDismiss: () -> Unit = { },

View file

@ -81,7 +81,7 @@ private fun TakeScreenshot(
} }
@Composable @Composable
fun RageshakeDialogContent( private fun RageshakeDialogContent(
onNoClicked: () -> Unit = { }, onNoClicked: () -> Unit = { },
onDisableClicked: () -> Unit = { }, onDisableClicked: () -> Unit = { },
onYesClicked: () -> Unit = { }, onYesClicked: () -> Unit = { },

View file

@ -43,7 +43,7 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.components.preferences.PreferenceRow import io.element.android.libraries.designsystem.components.preferences.PreferenceRow
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground
@ -72,7 +72,7 @@ fun BugReportView(
} }
Box(modifier = modifier) { Box(modifier = modifier) {
PreferenceView( PreferencePage(
title = stringResource(id = CommonStrings.common_report_a_bug), title = stringResource(id = CommonStrings.common_report_a_bug),
onBackPressed = onBackPressed onBackPressed = onBackPressed
) { ) {

View file

@ -183,7 +183,7 @@ fun RoomDetailsView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
internal fun RoomDetailsTopBar( private fun RoomDetailsTopBar(
goBack: () -> Unit, goBack: () -> Unit,
onActionClicked: (RoomDetailsAction) -> Unit, onActionClicked: (RoomDetailsAction) -> Unit,
showEdit: Boolean, showEdit: Boolean,
@ -220,7 +220,7 @@ internal fun RoomDetailsTopBar(
} }
@Composable @Composable
internal fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, modifier: Modifier = Modifier) { private fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, modifier: Modifier = Modifier) {
Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
val roomNotificationSettings = state.roomNotificationSettings val roomNotificationSettings = state.roomNotificationSettings
if (state.canShowNotificationSettings && roomNotificationSettings != null) { if (state.canShowNotificationSettings && roomNotificationSettings != null) {
@ -252,7 +252,7 @@ internal fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit
} }
@Composable @Composable
internal fun RoomHeaderSection( private fun RoomHeaderSection(
avatarUrl: String?, avatarUrl: String?,
roomId: String, roomId: String,
roomName: String, roomName: String,
@ -289,7 +289,7 @@ internal fun RoomHeaderSection(
} }
@Composable @Composable
internal fun TopicSection( private fun TopicSection(
roomTopic: RoomTopicState, roomTopic: RoomTopicState,
onActionClicked: (RoomDetailsAction) -> Unit, onActionClicked: (RoomDetailsAction) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
@ -315,7 +315,7 @@ internal fun TopicSection(
} }
@Composable @Composable
internal fun NotificationSection( private fun NotificationSection(
isDefaultMode: Boolean, isDefaultMode: Boolean,
openRoomNotificationSettings: () -> Unit, openRoomNotificationSettings: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
@ -336,7 +336,7 @@ internal fun NotificationSection(
} }
@Composable @Composable
internal fun MembersSection( private fun MembersSection(
memberCount: Long, memberCount: Long,
openRoomMemberList: () -> Unit, openRoomMemberList: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -352,7 +352,7 @@ internal fun MembersSection(
} }
@Composable @Composable
internal fun InviteSection( private fun InviteSection(
invitePeople: () -> Unit, invitePeople: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@ -366,7 +366,7 @@ internal fun InviteSection(
} }
@Composable @Composable
internal fun SecuritySection(modifier: Modifier = Modifier) { private fun SecuritySection(modifier: Modifier = Modifier) {
PreferenceCategory(title = stringResource(R.string.screen_room_details_security_title), modifier = modifier) { PreferenceCategory(title = stringResource(R.string.screen_room_details_security_title), modifier = modifier) {
PreferenceText( PreferenceText(
title = stringResource(R.string.screen_room_details_encryption_enabled_title), title = stringResource(R.string.screen_room_details_encryption_enabled_title),
@ -377,7 +377,7 @@ internal fun SecuritySection(modifier: Modifier = Modifier) {
} }
@Composable @Composable
internal fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = Modifier) { private fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = Modifier) {
PreferenceCategory(showDivider = false, modifier = modifier) { PreferenceCategory(showDivider = false, modifier = modifier) {
PreferenceText( PreferenceText(
title = stringResource(R.string.screen_room_details_leave_room_title), title = stringResource(R.string.screen_room_details_leave_room_title),

View file

@ -0,0 +1,77 @@
/*
* 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.features.roomdetails.impl.blockuser
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.features.roomdetails.impl.R
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
@Composable
fun BlockUserDialogs(state: RoomMemberDetailsState) {
when (state.displayConfirmationDialog) {
null -> Unit
RoomMemberDetailsState.ConfirmationDialog.Block -> {
BlockConfirmationDialog(
onBlockAction = {
state.eventSink(
RoomMemberDetailsEvents.BlockUser(
needsConfirmation = false
)
)
},
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
)
}
RoomMemberDetailsState.ConfirmationDialog.Unblock -> {
UnblockConfirmationDialog(
onUnblockAction = {
state.eventSink(
RoomMemberDetailsEvents.UnblockUser(
needsConfirmation = false
)
)
},
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
)
}
}
}
@Composable
private fun BlockConfirmationDialog(onBlockAction: () -> Unit, onDismiss: () -> Unit) {
ConfirmationDialog(
title = stringResource(R.string.screen_dm_details_block_user),
content = stringResource(R.string.screen_dm_details_block_alert_description),
submitText = stringResource(R.string.screen_dm_details_block_alert_action),
onSubmitClicked = onBlockAction,
onDismiss = onDismiss
)
}
@Composable
private fun UnblockConfirmationDialog(onUnblockAction: () -> Unit, onDismiss: () -> Unit) {
ConfirmationDialog(
title = stringResource(R.string.screen_dm_details_unblock_user),
content = stringResource(R.string.screen_dm_details_unblock_alert_description),
submitText = stringResource(R.string.screen_dm_details_unblock_alert_action),
onSubmitClicked = onUnblockAction,
onDismiss = onDismiss
)
}

View file

@ -27,7 +27,6 @@ import io.element.android.features.roomdetails.impl.members.details.RoomMemberDe
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.components.preferences.PreferenceText
@ -85,44 +84,3 @@ private fun PreferenceBlockUser(
) )
} }
} }
@Composable
internal fun BlockUserDialogs(state: RoomMemberDetailsState) {
when (state.displayConfirmationDialog) {
null -> Unit
RoomMemberDetailsState.ConfirmationDialog.Block -> {
BlockConfirmationDialog(
onBlockAction = { state.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false)) },
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
)
}
RoomMemberDetailsState.ConfirmationDialog.Unblock -> {
UnblockConfirmationDialog(
onUnblockAction = { state.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false)) },
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
)
}
}
}
@Composable
internal fun BlockConfirmationDialog(onBlockAction: () -> Unit, onDismiss: () -> Unit) {
ConfirmationDialog(
title = stringResource(R.string.screen_dm_details_block_user),
content = stringResource(R.string.screen_dm_details_block_alert_description),
submitText = stringResource(R.string.screen_dm_details_block_alert_action),
onSubmitClicked = onBlockAction,
onDismiss = onDismiss
)
}
@Composable
internal fun UnblockConfirmationDialog(onUnblockAction: () -> Unit, onDismiss: () -> Unit) {
ConfirmationDialog(
title = stringResource(R.string.screen_dm_details_unblock_user),
content = stringResource(R.string.screen_dm_details_unblock_alert_description),
submitText = stringResource(R.string.screen_dm_details_unblock_alert_action),
onSubmitClicked = onUnblockAction,
onDismiss = onDismiss
)
}

View file

@ -111,7 +111,7 @@ fun RoomInviteMembersView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun RoomInviteMembersTopBar( private fun RoomInviteMembersTopBar(
canSend: Boolean, canSend: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {}, onBackPressed: () -> Unit = {},

View file

@ -16,49 +16,27 @@
package io.element.android.features.roomdetails.impl.members.details package io.element.android.features.roomdetails.impl.members.details
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets 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.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ChatBubbleOutline
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs
import io.element.android.features.roomdetails.impl.blockuser.BlockUserSection import io.element.android.features.roomdetails.impl.blockuser.BlockUserSection
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.button.MainActionButton
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable @Composable
@ -103,51 +81,9 @@ fun RoomMemberDetailsView(
} }
} }
/*
@Composable @Composable
internal fun RoomMemberHeaderSection( private fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier = Modifier) {
avatarUrl: String?,
userId: String,
userName: String?,
modifier: Modifier = Modifier
) {
Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Box(modifier = Modifier.size(70.dp)) {
Avatar(
avatarData = AvatarData(userId, userName, avatarUrl, AvatarSize.UserHeader),
modifier = Modifier.fillMaxSize()
)
}
Spacer(modifier = Modifier.height(24.dp))
if (userName != null) {
Text(text = userName, style = ElementTheme.typography.fontHeadingLgBold)
Spacer(modifier = Modifier.height(6.dp))
}
Text(
text = userId,
style = ElementTheme.typography.fontBodyLgRegular,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(40.dp))
}
}
@Composable
internal fun RoomMemberMainActionsSection(onShareUser: () -> Unit, modifier: Modifier = Modifier) {
Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
MainActionButton(
title = stringResource(CommonStrings.action_share),
iconResourceId = CommonDrawables.ic_compound_share_android,
onClick = onShareUser
)
}
}
@Composable
internal fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier = Modifier) {
PreferenceCategory(modifier = modifier) { PreferenceCategory(modifier = modifier) {
PreferenceText( PreferenceText(
title = stringResource(CommonStrings.action_send_message), title = stringResource(CommonStrings.action_send_message),
@ -156,6 +92,7 @@ internal fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier =
) )
} }
} }
*/
@PreviewWithLargeHeight @PreviewWithLargeHeight
@Composable @Composable

View file

@ -0,0 +1,69 @@
/*
* 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.features.roomdetails.impl.members.details
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@Composable
fun RoomMemberHeaderSection(
avatarUrl: String?,
userId: String,
userName: String?,
modifier: Modifier = Modifier
) {
Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Box(modifier = Modifier.size(70.dp)) {
Avatar(
avatarData = AvatarData(userId, userName, avatarUrl, AvatarSize.UserHeader),
modifier = Modifier.fillMaxSize()
)
}
Spacer(modifier = Modifier.height(24.dp))
if (userName != null) {
Text(text = userName, style = ElementTheme.typography.fontHeadingLgBold)
Spacer(modifier = Modifier.height(6.dp))
}
Text(
text = userId,
style = ElementTheme.typography.fontBodyLgRegular,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(40.dp))
}
}

View file

@ -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.features.roomdetails.impl.members.details
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.libraries.designsystem.components.button.MainActionButton
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun RoomMemberMainActionsSection(onShareUser: () -> Unit, modifier: Modifier = Modifier) {
Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
MainActionButton(
title = stringResource(CommonStrings.action_share),
iconResourceId = CommonDrawables.ic_compound_share_android,
onClick = onShareUser
)
}
}

View file

@ -108,7 +108,7 @@ fun RoomNotificationSettingsView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun RoomNotificationSettingsTopBar( private fun RoomNotificationSettingsTopBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {}, onBackPressed: () -> Unit = {},
) { ) {
@ -125,7 +125,7 @@ fun RoomNotificationSettingsTopBar(
} }
@Composable @Composable
fun RoomNotificationSettingsOptions( private fun RoomNotificationSettingsOptions(
selected: RoomNotificationMode?, selected: RoomNotificationMode?,
enabled: Boolean, enabled: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

View file

@ -33,8 +33,8 @@ import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.user.getCurrentUser import io.element.android.libraries.matrix.api.user.getCurrentUser

View file

@ -19,7 +19,7 @@ package io.element.android.features.roomlist.impl
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.LeaveRoomState
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList

View file

@ -22,7 +22,7 @@ import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryPlaceholders import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryPlaceholders
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser

View file

@ -59,8 +59,8 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@Composable @Composable
@ -124,7 +124,7 @@ fun RoomListView(
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable @Composable
fun RoomListContent( private fun RoomListContent(
state: RoomListState, state: RoomListState,
onVerifyClicked: () -> Unit, onVerifyClicked: () -> Unit,
onRoomClicked: (RoomId) -> Unit, onRoomClicked: (RoomId) -> Unit,

View file

@ -85,7 +85,7 @@ internal fun RoomSummaryRow(
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
internal fun RoomSummaryRealRow( private fun RoomSummaryRealRow(
room: RoomListRoomSummary, room: RoomListRoomSummary,
onClick: (RoomListRoomSummary) -> Unit, onClick: (RoomListRoomSummary) -> Unit,
onLongClick: (RoomListRoomSummary) -> Unit, onLongClick: (RoomListRoomSummary) -> Unit,

View file

@ -100,7 +100,7 @@ internal fun RoomListSearchResultView(
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable @Composable
internal fun RoomListSearchResultContent( private fun RoomListSearchResultContent(
state: RoomListState, state: RoomListState,
onRoomClicked: (RoomId) -> Unit, onRoomClicked: (RoomId) -> Unit,
onRoomLongClicked: (RoomListRoomSummary) -> Unit, onRoomLongClicked: (RoomListRoomSummary) -> Unit,

View file

@ -34,7 +34,7 @@ import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormat
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient

View file

@ -89,7 +89,7 @@ fun VerifySelfSessionView(
} }
@Composable @Composable
internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = Modifier) { private fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = Modifier) {
val iconResourceId = when (verificationFlowStep) { val iconResourceId = when (verificationFlowStep) {
FlowStep.Initial -> R.drawable.ic_verification_devices FlowStep.Initial -> R.drawable.ic_verification_devices
FlowStep.Canceled -> R.drawable.ic_verification_warning FlowStep.Canceled -> R.drawable.ic_verification_warning
@ -118,7 +118,7 @@ internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier =
} }
@Composable @Composable
internal fun Content(flowState: FlowStep, modifier: Modifier = Modifier) { private fun Content(flowState: FlowStep, modifier: Modifier = Modifier) {
Column(modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) { Column(modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) {
when (flowState) { when (flowState) {
FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit
@ -129,14 +129,14 @@ internal fun Content(flowState: FlowStep, modifier: Modifier = Modifier) {
} }
@Composable @Composable
internal fun ContentWaiting(modifier: Modifier = Modifier) { private fun ContentWaiting(modifier: Modifier = Modifier) {
Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
CircularProgressIndicator() CircularProgressIndicator()
} }
} }
@Composable @Composable
internal fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: Modifier = Modifier) { private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: Modifier = Modifier) {
// We want each row to have up to 4 emojis // We want each row to have up to 4 emojis
val rows = verificationFlowStep.emojiList.chunked(4) val rows = verificationFlowStep.emojiList.chunked(4)
Column(modifier = modifier.fillMaxWidth()) { Column(modifier = modifier.fillMaxWidth()) {
@ -155,7 +155,7 @@ internal fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier
} }
@Composable @Composable
internal fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifier) { private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifier) {
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) { Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) {
Text( Text(
text = emoji.code, text = emoji.code,
@ -173,7 +173,7 @@ internal fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifi
} }
@Composable @Composable
internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) { private fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) {
val verificationViewState = screenState.verificationFlowStep val verificationViewState = screenState.verificationFlowStep
val eventSink = screenState.eventSink val eventSink = screenState.eventSink

View file

@ -71,7 +71,7 @@ fun BlurHashAsyncImage(
} }
@Composable @Composable
fun BlurHashImage( private fun BlurHashImage(
blurHash: String?, blurHash: String?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
contentDescription: String? = null, contentDescription: String? = null,

View file

@ -72,7 +72,7 @@ fun ListDialog(
} }
@Composable @Composable
internal fun ListDialogContent( private fun ListDialogContent(
listItems: LazyListScope.() -> Unit, listItems: LazyListScope.() -> Unit,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onSubmitClicked: () -> Unit, onSubmitClicked: () -> Unit,

View file

@ -78,7 +78,7 @@ fun MultipleSelectionDialog(
} }
@Composable @Composable
internal fun MultipleSelectionDialogContent( private fun MultipleSelectionDialogContent(
options: ImmutableList<ListOption>, options: ImmutableList<ListOption>,
confirmButtonTitle: String, confirmButtonTitle: String,
onConfirmClicked: (List<Int>) -> Unit, onConfirmClicked: (List<Int>) -> Unit,

View file

@ -74,7 +74,7 @@ fun SingleSelectionDialog(
} }
@Composable @Composable
internal fun SingleSelectionDialogContent( private fun SingleSelectionDialogContent(
options: ImmutableList<ListOption>, options: ImmutableList<ListOption>,
onOptionSelected: (Int) -> Unit, onOptionSelected: (Int) -> Unit,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,

View file

@ -52,7 +52,7 @@ fun PreferenceCategory(
} }
@Composable @Composable
fun PreferenceCategoryTitle(title: String, modifier: Modifier = Modifier) { private fun PreferenceCategoryTitle(title: String, modifier: Modifier = Modifier) {
Text( Text(
modifier = modifier.padding( modifier = modifier.padding(
top = 20.dp, top = 20.dp,

View file

@ -44,7 +44,7 @@ import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
fun PreferenceView( fun PreferencePage(
title: String, title: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {}, onBackPressed: () -> Unit = {},
@ -79,7 +79,7 @@ fun PreferenceView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun PreferenceTopAppBar( private fun PreferenceTopAppBar(
title: String, title: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {}, onBackPressed: () -> Unit = {},
@ -104,7 +104,7 @@ fun PreferenceTopAppBar(
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun PreferenceViewPreview() = ElementPreview { internal fun PreferenceViewPreview() = ElementPreview {
PreferenceView( PreferencePage(
title = "Preference screen" title = "Preference screen"
) { ) {
PreferenceCategory( PreferenceCategory(

View file

@ -16,90 +16,11 @@
package io.element.android.libraries.designsystem.preview package io.element.android.libraries.designsystem.preview
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.theme.ElementTheme
@Composable
fun ElementPreviewLight(
showBackground: Boolean = true,
content: @Composable () -> Unit
) {
ElementPreview(
darkTheme = false,
showBackground = showBackground,
content = content
)
}
@Composable
fun ElementPreviewDark(
showBackground: Boolean = true,
content: @Composable () -> Unit
) {
ElementPreview(
darkTheme = true,
showBackground = showBackground,
content = content
)
}
@Composable
@Suppress("ModifierMissing")
fun ElementThemedPreview(
showBackground: Boolean = true,
vertical: Boolean = true,
content: @Composable () -> Unit,
) {
Box(
modifier = Modifier
.background(Color.Gray)
.padding(4.dp)
) {
if (vertical) {
Column {
ElementPreview(
darkTheme = false,
showBackground = showBackground,
content = content,
)
Spacer(modifier = Modifier.height(4.dp))
ElementPreview(
darkTheme = true,
showBackground = showBackground,
content = content
)
}
} else {
Row {
ElementPreview(
darkTheme = false,
showBackground = showBackground,
content = content,
)
Spacer(modifier = Modifier.width(4.dp))
ElementPreview(
darkTheme = true,
showBackground = showBackground,
content = content
)
}
}
}
}
@Composable @Composable
@Suppress("ModifierMissing") @Suppress("ModifierMissing")
fun ElementPreview( fun ElementPreview(

View file

@ -0,0 +1,31 @@
/*
* 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
@Composable
fun ElementPreviewDark(
showBackground: Boolean = true,
content: @Composable () -> Unit
) {
ElementPreview(
darkTheme = true,
showBackground = showBackground,
content = content
)
}

View file

@ -0,0 +1,31 @@
/*
* 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
@Composable
fun ElementPreviewLight(
showBackground: Boolean = true,
content: @Composable () -> Unit
) {
ElementPreview(
darkTheme = false,
showBackground = showBackground,
content = content
)
}

View file

@ -0,0 +1,74 @@
/*
* 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.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
@Suppress("ModifierMissing")
fun ElementThemedPreview(
showBackground: Boolean = true,
vertical: Boolean = true,
content: @Composable () -> Unit,
) {
Box(
modifier = Modifier
.background(Color.Gray)
.padding(4.dp)
) {
if (vertical) {
Column {
ElementPreview(
darkTheme = false,
showBackground = showBackground,
content = content,
)
Spacer(modifier = Modifier.height(4.dp))
ElementPreview(
darkTheme = true,
showBackground = showBackground,
content = content
)
}
} else {
Row {
ElementPreview(
darkTheme = false,
showBackground = showBackground,
content = content,
)
Spacer(modifier = Modifier.width(4.dp))
ElementPreview(
darkTheme = true,
showBackground = showBackground,
content = content
)
}
}
}
}

View file

@ -52,7 +52,7 @@ fun Dp.applyScaleUp(): Dp = with(LocalDensity.current) {
@Preview @Preview
@Composable @Composable
internal fun DpScalePreview_0_75f() = WithFontScale(0.75f) { internal fun DpScale_0_75f_Preview() = WithFontScale(0.75f) {
ElementPreviewLight { ElementPreviewLight {
val fontSizeInDp = 16.dp val fontSizeInDp = 16.dp
Column( Column(
@ -77,7 +77,7 @@ internal fun DpScalePreview_0_75f() = WithFontScale(0.75f) {
@Preview @Preview
@Composable @Composable
internal fun DpScalePreview_1_0f() = WithFontScale(1f) { internal fun DpScale_1_0f_Preview() = WithFontScale(1f) {
ElementPreviewLight { ElementPreviewLight {
val fontSizeInDp = 16.dp val fontSizeInDp = 16.dp
Column( Column(
@ -102,7 +102,7 @@ internal fun DpScalePreview_1_0f() = WithFontScale(1f) {
@Preview @Preview
@Composable @Composable
internal fun DpScalePreview_1_5f() = WithFontScale(1.5f) { internal fun DpScale_1_5f_Preview() = WithFontScale(1.5f) {
ElementPreviewLight { ElementPreviewLight {
val fontSizeInDp = 16.dp val fontSizeInDp = 16.dp
Column( Column(

View file

@ -117,7 +117,7 @@ fun TextButton(
) )
@Composable @Composable
internal fun ButtonInternal( private fun ButtonInternal(
text: String, text: String,
onClick: () -> Unit, onClick: () -> Unit,
style: ButtonStyle, style: ButtonStyle,

View file

@ -0,0 +1,127 @@
/*
* 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.theme.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.theme.ElementTheme
// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1
/**
* List section header.
* @param title The title of the section.
* @param modifier The modifier to be applied to the section.
* @param hasDivider Whether to show a divider above the section or not. Default is `true`.
* @param description A description for the section. It's empty by default.
*/
@Composable
fun ListSectionHeader(
title: String,
modifier: Modifier = Modifier,
hasDivider: Boolean = true,
description: @Composable () -> Unit = {},
) {
Column(modifier.fillMaxWidth()) {
if (hasDivider) {
HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
}
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = title,
style = ElementTheme.typography.fontBodyLgMedium,
color = ElementTheme.colors.textPrimary,
)
CompositionLocalProvider(
LocalTextStyle provides ElementTheme.typography.fontBodySmRegular,
LocalContentColor provides ElementTheme.colors.textSecondary,
) {
description()
}
}
}
}
@Preview(group = PreviewGroup.ListSections, name = "List section header")
@Composable
internal fun ListSectionHeaderPreview() {
ElementThemedPreview {
ListSectionHeader(
title = "List section",
hasDivider = false,
)
}
}
@Preview(group = PreviewGroup.ListSections, name = "List section header with divider")
@Composable
internal fun ListSectionHeaderWithDividerPreview() {
ElementThemedPreview {
ListSectionHeader(
title = "List section",
hasDivider = true,
)
}
}
@Preview(group = PreviewGroup.ListSections, name = "List section header with description")
@Composable
internal fun ListSectionHeaderWithDescriptionPreview() {
ElementThemedPreview {
ListSectionHeader(
title = "List section",
description = {
ListSupportingText(
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
contentPadding = ListSupportingTextDefaults.Padding.None,
)
},
hasDivider = false,
)
}
}
@Preview(group = PreviewGroup.ListSections, name = "List section header with description and divider")
@Composable
internal fun ListSectionHeaderWithDescriptionAndDividerPreview() {
ElementThemedPreview {
ListSectionHeader(
title = "List section",
description = {
ListSupportingText(
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
contentPadding = ListSupportingTextDefaults.Padding.None,
)
},
hasDivider = true,
)
}
}

View file

@ -16,15 +16,10 @@
package io.element.android.libraries.designsystem.theme.components package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ExperimentalTextApi import androidx.compose.ui.text.ExperimentalTextApi
@ -40,43 +35,6 @@ import io.element.android.libraries.theme.ElementTheme
// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1 // Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1
/**
* List section header.
* @param title The title of the section.
* @param modifier The modifier to be applied to the section.
* @param hasDivider Whether to show a divider above the section or not. Default is `true`.
* @param description A description for the section. It's empty by default.
*/
@Composable
fun ListSectionHeader(
title: String,
modifier: Modifier = Modifier,
hasDivider: Boolean = true,
description: @Composable () -> Unit = {},
) {
Column(modifier.fillMaxWidth()) {
if (hasDivider) {
HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
}
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = title,
style = ElementTheme.typography.fontBodyLgMedium,
color = ElementTheme.colors.textPrimary,
)
CompositionLocalProvider(
LocalTextStyle provides ElementTheme.typography.fontBodySmRegular,
LocalContentColor provides ElementTheme.colors.textSecondary,
) {
description()
}
}
}
}
/** /**
* List supporting text item. Used to display an explanation in the list with a pre-formatted style. * List supporting text item. Used to display an explanation in the list with a pre-formatted style.
* @param text The text to display. * @param text The text to display.
@ -167,68 +125,6 @@ object ListSupportingTextDefaults {
} }
} }
// region: List header previews
@Preview(group = PreviewGroup.ListSections, name = "List section header")
@Composable
internal fun ListSectionHeaderPreview() {
ElementThemedPreview {
ListSectionHeader(
title = "List section",
hasDivider = false,
)
}
}
@Preview(group = PreviewGroup.ListSections, name = "List section header with divider")
@Composable
internal fun ListSectionHeaderWithDividerPreview() {
ElementThemedPreview {
ListSectionHeader(
title = "List section",
hasDivider = true,
)
}
}
@Preview(group = PreviewGroup.ListSections, name = "List section header with description")
@Composable
internal fun ListSectionHeaderWithDescriptionPreview() {
ElementThemedPreview {
ListSectionHeader(
title = "List section",
description = {
ListSupportingText(
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
contentPadding = ListSupportingTextDefaults.Padding.None,
)
},
hasDivider = false,
)
}
}
@Preview(group = PreviewGroup.ListSections, name = "List section header with description and divider")
@Composable
internal fun ListSectionHeaderWithDescriptionAndDividerPreview() {
ElementThemedPreview {
ListSectionHeader(
title = "List section",
description = {
ListSupportingText(
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
contentPadding = ListSupportingTextDefaults.Padding.None,
)
},
hasDivider = true,
)
}
}
// endregion
// region: List supporting text previews
@Preview(group = PreviewGroup.ListSections, name = "List supporting text - no padding") @Preview(group = PreviewGroup.ListSections, name = "List supporting text - no padding")
@Composable @Composable
internal fun ListSupportingTextNoPaddingPreview() { internal fun ListSupportingTextNoPaddingPreview() {
@ -298,5 +194,3 @@ internal fun ListSupportingTextCustomPaddingPreview() {
} }
} }
} }
// endregion

View file

@ -193,11 +193,11 @@ sealed interface SearchBarResultState<in T> {
@Preview(group = PreviewGroup.Search) @Preview(group = PreviewGroup.Search)
@Composable @Composable
internal fun SearchBarPreviewInactive() = ElementThemedPreview { ContentToPreview() } internal fun SearchBarInactivePreview() = ElementThemedPreview { ContentToPreview() }
@Preview(group = PreviewGroup.Search) @Preview(group = PreviewGroup.Search)
@Composable @Composable
internal fun SearchBarPreviewActiveEmptyQuery() = ElementThemedPreview { internal fun SearchBarActiveEmptyQueryPreview() = ElementThemedPreview {
ContentToPreview( ContentToPreview(
query = "", query = "",
active = true, active = true,
@ -206,7 +206,7 @@ internal fun SearchBarPreviewActiveEmptyQuery() = ElementThemedPreview {
@Preview(group = PreviewGroup.Search) @Preview(group = PreviewGroup.Search)
@Composable @Composable
internal fun SearchBarPreviewActiveWithQuery() = ElementThemedPreview { internal fun SearchBarActiveWithQueryPreview() = ElementThemedPreview {
ContentToPreview( ContentToPreview(
query = "search term", query = "search term",
active = true, active = true,
@ -215,7 +215,7 @@ internal fun SearchBarPreviewActiveWithQuery() = ElementThemedPreview {
@Preview(group = PreviewGroup.Search) @Preview(group = PreviewGroup.Search)
@Composable @Composable
internal fun SearchBarPreviewActiveWithQueryNoBackButton() = ElementThemedPreview { internal fun SearchBarActiveWithQueryNoBackButtonPreview() = ElementThemedPreview {
ContentToPreview( ContentToPreview(
query = "search term", query = "search term",
active = true, active = true,
@ -225,7 +225,7 @@ internal fun SearchBarPreviewActiveWithQueryNoBackButton() = ElementThemedPrevie
@Preview(group = PreviewGroup.Search) @Preview(group = PreviewGroup.Search)
@Composable @Composable
internal fun SearchBarPreviewActiveWithNoResults() = ElementThemedPreview { internal fun SearchBarActiveWithNoResultsPreview() = ElementThemedPreview {
ContentToPreview( ContentToPreview(
query = "search term", query = "search term",
active = true, active = true,
@ -235,7 +235,7 @@ internal fun SearchBarPreviewActiveWithNoResults() = ElementThemedPreview {
@Preview(group = PreviewGroup.Search) @Preview(group = PreviewGroup.Search)
@Composable @Composable
internal fun SearchBarPreviewActiveWithContent() = ElementThemedPreview { internal fun SearchBarActiveWithContentPreview() = ElementThemedPreview {
ContentToPreview( ContentToPreview(
query = "search term", query = "search term",
active = true, active = true,

View file

@ -30,13 +30,13 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
@Preview(group = PreviewGroup.DateTimePickers) @Preview(group = PreviewGroup.DateTimePickers)
@Composable @Composable
internal fun DatePickerPreviewLight() { internal fun DatePickerLightPreview() {
ElementPreviewLight { ContentToPreview() } ElementPreviewLight { ContentToPreview() }
} }
@Preview(group = PreviewGroup.DateTimePickers) @Preview(group = PreviewGroup.DateTimePickers)
@Composable @Composable
internal fun DatePickerPreviewDark() { internal fun DatePickerDarkPreview() {
ElementPreviewDark { ContentToPreview() } ElementPreviewDark { ContentToPreview() }
} }

View file

@ -55,7 +55,7 @@ internal fun TimePickerHorizontalPreview() {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.DateTimePickers) @Preview(group = PreviewGroup.DateTimePickers)
@Composable @Composable
internal fun TimePickerVerticalPreviewLight() { internal fun TimePickerVerticalLightPreview() {
ElementPreviewLight { ElementPreviewLight {
AlertDialogContent( AlertDialogContent(
buttons = { /*TODO*/ }, buttons = { /*TODO*/ },
@ -77,7 +77,7 @@ internal fun TimePickerVerticalPreviewLight() {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.DateTimePickers) @Preview(group = PreviewGroup.DateTimePickers)
@Composable @Composable
internal fun TimePickerVerticalPreviewDark() { internal fun TimePickerVerticalDarkPreview() {
val pickerState = rememberTimePickerState( val pickerState = rememberTimePickerState(
initialHour = 12, initialHour = 12,
initialMinute = 0, initialMinute = 0,

View file

@ -14,11 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
package io.element.android.libraries.designsystem.utils package io.element.android.libraries.designsystem.utils.snackbar
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -26,11 +23,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.button.ButtonVisuals
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.Snackbar import io.element.android.libraries.designsystem.theme.components.Snackbar
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.currentCoroutineContext
@ -38,7 +31,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import java.util.concurrent.atomic.AtomicBoolean
/** /**
* A global dispatcher of [SnackbarMessage] to be displayed in [Snackbar] via a [SnackbarHostState]. * A global dispatcher of [SnackbarMessage] to be displayed in [Snackbar] via a [SnackbarHostState].
@ -78,23 +70,6 @@ fun SnackbarDispatcher.collectSnackbarMessageAsState(): State<SnackbarMessage?>
return snackbarMessage.collectAsState(initial = null) return snackbarMessage.collectAsState(initial = null)
} }
@Composable
fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
androidx.compose.material3.SnackbarHost(hostState, modifier) { data ->
Snackbar(
modifier = Modifier.padding(12.dp), // Add default padding
message = data.visuals.message,
action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) },
dismissAction = if (data.visuals.withDismissAction) {
ButtonVisuals.Icon(
IconSource.Resource(CommonDrawables.ic_compound_close),
data::dismiss
)
} else null,
)
}
}
/** /**
* Helper method to display a [SnackbarMessage] in a [SnackbarHostState] handling cancellations. * Helper method to display a [SnackbarMessage] in a [SnackbarHostState] handling cancellations.
*/ */
@ -127,19 +102,3 @@ fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostSt
} }
return snackbarHostState return snackbarHostState
} }
/**
* A message to be displayed in a [Snackbar].
* @param messageResId The message to be displayed.
* @param duration The duration of the message. The default value is [SnackbarDuration.Short].
* @param actionResId The action text to be displayed. The default value is `null`.
* @param isDisplayed Used to track if the current message is already displayed or not.
* @param action The action to be performed when the action is clicked.
*/
data class SnackbarMessage(
@StringRes val messageResId: Int,
val duration: SnackbarDuration = SnackbarDuration.Short,
@StringRes val actionResId: Int? = null,
val isDisplayed: AtomicBoolean = AtomicBoolean(false),
val action: () -> Unit = {},
)

View file

@ -0,0 +1,44 @@
/*
* 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.utils.snackbar
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.button.ButtonVisuals
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.Snackbar
import io.element.android.libraries.designsystem.utils.CommonDrawables
@Composable
fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
androidx.compose.material3.SnackbarHost(hostState, modifier) { data ->
Snackbar(
modifier = Modifier.padding(12.dp), // Add default padding
message = data.visuals.message,
action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) },
dismissAction = if (data.visuals.withDismissAction) {
ButtonVisuals.Icon(
IconSource.Resource(CommonDrawables.ic_compound_close),
data::dismiss
)
} else null,
)
}
}

View file

@ -0,0 +1,37 @@
/*
* 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.utils.snackbar
import androidx.annotation.StringRes
import androidx.compose.material3.SnackbarDuration
import java.util.concurrent.atomic.AtomicBoolean
/**
* A message to be displayed in a [Snackbar].
* @param messageResId The message to be displayed.
* @param duration The duration of the message. The default value is [SnackbarDuration.Short].
* @param actionResId The action text to be displayed. The default value is `null`.
* @param isDisplayed Used to track if the current message is already displayed or not.
* @param action The action to be performed when the action is clicked.
*/
data class SnackbarMessage(
@StringRes val messageResId: Int,
val duration: SnackbarDuration = SnackbarDuration.Short,
@StringRes val actionResId: Int? = null,
val isDisplayed: AtomicBoolean = AtomicBoolean(false),
val action: () -> Unit = {},
)

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package io.element.android.libraries.designsystem.utils package io.element.android.libraries.designsystem.utils.snackbar
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat

View file

@ -46,7 +46,7 @@ fun FeatureListView(
} }
@Composable @Composable
fun FeaturePreferenceView( private fun FeaturePreferenceView(
feature: FeatureUiModel, feature: FeatureUiModel,
onCheckedChange: (Boolean) -> Unit, onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier

View file

@ -16,22 +16,13 @@
package io.element.android.libraries.matrix.ui.components package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.model.getBestName import io.element.android.libraries.matrix.ui.model.getBestName
@ -54,41 +45,6 @@ fun CheckableMatrixUserRow(
enabled = enabled, enabled = enabled,
) )
@Composable
fun CheckableUserRow(
checked: Boolean,
avatarData: AvatarData,
name: String,
subtext: String?,
modifier: Modifier = Modifier,
onCheckedChange: (Boolean) -> Unit = {},
enabled: Boolean = true,
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable(role = Role.Checkbox, enabled = enabled) {
onCheckedChange(!checked)
},
verticalAlignment = Alignment.CenterVertically,
) {
UserRow(
modifier = Modifier.weight(1f),
avatarData = avatarData,
name = name,
subtext = subtext,
)
Checkbox(
modifier = Modifier
.padding(end = 16.dp),
checked = checked,
onCheckedChange = null,
enabled = enabled,
)
}
}
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun CheckableMatrixUserRowPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview { internal fun CheckableMatrixUserRowPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview {

View file

@ -0,0 +1,82 @@
/*
* 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.matrix.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.matrix.ui.model.getAvatarData
@Composable
fun CheckableUnresolvedUserRow(
checked: Boolean,
avatarData: AvatarData,
id: String,
modifier: Modifier = Modifier,
onCheckedChange: (Boolean) -> Unit = {},
enabled: Boolean = true,
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable(role = Role.Checkbox, enabled = enabled) {
onCheckedChange(!checked)
},
verticalAlignment = Alignment.CenterVertically,
) {
UnresolvedUserRow(
modifier = Modifier.weight(1f),
avatarData = avatarData,
id = id,
)
Checkbox(
modifier = Modifier.padding(end = 16.dp),
checked = checked,
onCheckedChange = null,
enabled = enabled,
)
}
}
@Preview
@Composable
internal fun CheckableUnresolvedUserRowPreview() = ElementThemedPreview {
val matrixUser = aMatrixUser()
Column {
CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value)
HorizontalDivider()
CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value)
HorizontalDivider()
CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
HorizontalDivider()
CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
}
}

View file

@ -0,0 +1,64 @@
/*
* 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.matrix.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.theme.components.Checkbox
@Composable
fun CheckableUserRow(
checked: Boolean,
avatarData: AvatarData,
name: String,
subtext: String?,
modifier: Modifier = Modifier,
onCheckedChange: (Boolean) -> Unit = {},
enabled: Boolean = true,
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable(role = Role.Checkbox, enabled = enabled) {
onCheckedChange(!checked)
},
verticalAlignment = Alignment.CenterVertically,
) {
UserRow(
modifier = Modifier.weight(1f),
avatarData = avatarData,
name = name,
subtext = subtext,
)
Checkbox(
modifier = Modifier
.padding(end = 16.dp),
checked = checked,
onCheckedChange = null,
enabled = enabled,
)
}
}

View file

@ -16,28 +16,15 @@
package io.element.android.libraries.matrix.ui.components package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.model.getBestName import io.element.android.libraries.matrix.ui.model.getBestName
import io.element.android.libraries.theme.ElementTheme
@Composable @Composable
fun MatrixUserRow( fun MatrixUserRow(
@ -51,47 +38,6 @@ fun MatrixUserRow(
modifier = modifier, modifier = modifier,
) )
@Composable
fun UserRow(
avatarData: AvatarData,
name: String,
subtext: String?,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.heightIn(min = 56.dp)
.padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Avatar(avatarData)
Column(
modifier = Modifier
.padding(start = 12.dp),
) {
// Name
Text(
text = name,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.primary,
style = ElementTheme.typography.fontBodyLgRegular,
)
// Id
subtext?.let {
Text(
text = subtext,
color = MaterialTheme.colorScheme.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = ElementTheme.typography.fontBodySmRegular,
)
}
}
}
}
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun MatrixUserRowPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview { internal fun MatrixUserRowPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview {

View file

@ -16,7 +16,6 @@
package io.element.android.libraries.matrix.ui.components package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -29,7 +28,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -38,8 +36,6 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables
@ -101,58 +97,9 @@ fun UnresolvedUserRow(
} }
} }
@Preview
@Composable @Composable
fun CheckableUnresolvedUserRow( internal fun UnresolvedUserRowPreview() = ElementThemedPreview {
checked: Boolean, val matrixUser = aMatrixUser()
avatarData: AvatarData, UnresolvedUserRow(matrixUser.getAvatarData(size = AvatarSize.UserListItem), matrixUser.userId.value)
id: String,
modifier: Modifier = Modifier,
onCheckedChange: (Boolean) -> Unit = {},
enabled: Boolean = true,
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable(role = Role.Checkbox, enabled = enabled) {
onCheckedChange(!checked)
},
verticalAlignment = Alignment.CenterVertically,
) {
UnresolvedUserRow(
modifier = Modifier.weight(1f),
avatarData = avatarData,
id = id,
)
Checkbox(
modifier = Modifier.padding(end = 16.dp),
checked = checked,
onCheckedChange = null,
enabled = enabled,
)
}
} }
@Preview
@Composable
internal fun UnresolvedUserRowPreview() =
ElementThemedPreview {
val matrixUser = aMatrixUser()
UnresolvedUserRow(matrixUser.getAvatarData(size = AvatarSize.UserListItem), matrixUser.userId.value)
}
@Preview
@Composable
internal fun CheckableUnresolvedUserRowPreview() =
ElementThemedPreview {
val matrixUser = aMatrixUser()
Column {
CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value)
HorizontalDivider()
CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value)
HorizontalDivider()
CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
HorizontalDivider()
CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
}
}

View file

@ -0,0 +1,74 @@
/*
* 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.matrix.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@Composable
internal fun UserRow(
avatarData: AvatarData,
name: String,
subtext: String?,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.heightIn(min = 56.dp)
.padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Avatar(avatarData)
Column(
modifier = Modifier
.padding(start = 12.dp),
) {
// Name
Text(
text = name,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.primary,
style = ElementTheme.typography.fontBodyLgRegular,
)
// Id
subtext?.let {
Text(
text = subtext,
color = MaterialTheme.colorScheme.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = ElementTheme.typography.fontBodySmRegular,
)
}
}
}
}

View file

@ -26,7 +26,6 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.SideEffect import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -138,27 +137,7 @@ fun ElementTheme(
} }
} }
/** internal fun SystemUiController.applyTheme(
* Can be used to force a composable in dark theme.
* It will automatically change the system ui colors back to normal when leaving the composition.
*/
@Composable
fun ForcedDarkElementTheme(
lightStatusBar: Boolean = false,
content: @Composable () -> Unit,
) {
val systemUiController = rememberSystemUiController()
val colorScheme = MaterialTheme.colorScheme
val wasDarkTheme = !ElementTheme.colors.isLight
DisposableEffect(Unit) {
onDispose {
systemUiController.applyTheme(colorScheme, wasDarkTheme)
}
}
ElementTheme(darkTheme = true, lightStatusBar = lightStatusBar, content = content)
}
private fun SystemUiController.applyTheme(
colorScheme: ColorScheme, colorScheme: ColorScheme,
darkTheme: Boolean, darkTheme: Boolean,
) { ) {

View file

@ -0,0 +1,42 @@
/*
* 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.theme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import com.google.accompanist.systemuicontroller.rememberSystemUiController
/**
* Can be used to force a composable in dark theme.
* It will automatically change the system ui colors back to normal when leaving the composition.
*/
@Composable
fun ForcedDarkElementTheme(
lightStatusBar: Boolean = false,
content: @Composable () -> Unit,
) {
val systemUiController = rememberSystemUiController()
val colorScheme = MaterialTheme.colorScheme
val wasDarkTheme = !ElementTheme.colors.isLight
DisposableEffect(Unit) {
onDispose {
systemUiController.applyTheme(colorScheme, wasDarkTheme)
}
}
ElementTheme(darkTheme = true, lightStatusBar = lightStatusBar, content = content)
}

Some files were not shown because too many files have changed in this diff Show more