Merge pull request #211 from vector-im/feature/fre/start_chat_search_matrixid
[Start chat] Show a single result when searching for a matrixId
This commit is contained in:
1
changelog.d/211.wip
Normal file
1
changelog.d/211.wip
Normal file
@@ -0,0 +1 @@
|
||||
[Create and join rooms] Show a single result when searching for a matrixId
|
||||
@@ -16,7 +16,12 @@
|
||||
|
||||
package io.element.android.features.createroom.impl.root
|
||||
|
||||
import io.element.android.libraries.matrix.ui.model.MatrixUser
|
||||
|
||||
sealed interface CreateRoomRootEvents {
|
||||
data class UpdateSearchQuery(val query: String) : CreateRoomRootEvents
|
||||
data class StartDM(val matrixUser: MatrixUser) : CreateRoomRootEvents
|
||||
object CreateRoom : CreateRoomRootEvents
|
||||
object InvitePeople : CreateRoomRootEvents
|
||||
data class OnSearchActiveChanged(val active: Boolean) : CreateRoomRootEvents
|
||||
}
|
||||
|
||||
@@ -17,23 +17,76 @@
|
||||
package io.element.android.features.createroom.impl.root
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.core.MatrixPatterns
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.ui.model.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreateRoomRootPresenter @Inject constructor() : Presenter<CreateRoomRootState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): CreateRoomRootState {
|
||||
var isSearchActive by rememberSaveable { mutableStateOf(false) }
|
||||
var searchQuery by rememberSaveable { mutableStateOf("") }
|
||||
val searchResults: MutableState<ImmutableList<MatrixUser>> = remember {
|
||||
mutableStateOf(persistentListOf())
|
||||
}
|
||||
|
||||
fun handleEvents(event: CreateRoomRootEvents) {
|
||||
when (event) {
|
||||
is CreateRoomRootEvents.OnSearchActiveChanged -> isSearchActive = event.active
|
||||
is CreateRoomRootEvents.UpdateSearchQuery -> searchQuery = event.query
|
||||
is CreateRoomRootEvents.StartDM -> handleStartDM(event.matrixUser)
|
||||
CreateRoomRootEvents.CreateRoom -> Unit // Todo Handle create room action
|
||||
CreateRoomRootEvents.InvitePeople -> Unit // Todo Handle invite people action
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(searchQuery) {
|
||||
// Clear the search results before performing the search, manually add a fake result with the matrixId, if any
|
||||
searchResults.value = if (MatrixPatterns.isUserId(searchQuery)) {
|
||||
persistentListOf(MatrixUser(UserId(searchQuery)))
|
||||
} else {
|
||||
persistentListOf()
|
||||
}
|
||||
// Perform the search asynchronously
|
||||
if (searchQuery.isNotEmpty()) {
|
||||
searchResults.value = performSearch(searchQuery)
|
||||
}
|
||||
}
|
||||
|
||||
return CreateRoomRootState(
|
||||
eventSink = ::handleEvents
|
||||
eventSink = ::handleEvents,
|
||||
isSearchActive = isSearchActive,
|
||||
searchQuery = searchQuery,
|
||||
searchResults = searchResults.value,
|
||||
)
|
||||
}
|
||||
|
||||
private fun performSearch(query: String): ImmutableList<MatrixUser> {
|
||||
val isMatrixId = MatrixPatterns.isUserId(query)
|
||||
val results = mutableListOf<MatrixUser>()// TODO trigger /search request
|
||||
if (isMatrixId && results.none { it.id.value == query }) {
|
||||
val getProfileResult: MatrixUser? = null // TODO trigger /profile request
|
||||
val profile = getProfileResult ?: MatrixUser(UserId(query))
|
||||
results.add(0, profile)
|
||||
}
|
||||
return results.toImmutableList()
|
||||
}
|
||||
|
||||
private fun handleStartDM(matrixUser: MatrixUser) {
|
||||
Timber.d("handleStartDM: $matrixUser") // Todo handle start DM action
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
package io.element.android.features.createroom.impl.root
|
||||
|
||||
// TODO add your ui models. Remove the eventSink if you don't have events.
|
||||
import io.element.android.libraries.matrix.ui.model.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
// Do not use default value, so no member get forgotten in the presenters.
|
||||
data class CreateRoomRootState(
|
||||
val eventSink: (CreateRoomRootEvents) -> Unit
|
||||
val eventSink: (CreateRoomRootEvents) -> Unit,
|
||||
val isSearchActive: Boolean,
|
||||
val searchQuery: String,
|
||||
val searchResults: ImmutableList<MatrixUser>,
|
||||
)
|
||||
|
||||
@@ -17,15 +17,43 @@
|
||||
package io.element.android.features.createroom.impl.root
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.ui.model.MatrixUser
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRootState> {
|
||||
override val values: Sequence<CreateRoomRootState>
|
||||
get() = sequenceOf(
|
||||
aCreateRoomRootState(),
|
||||
// Add other state here
|
||||
aCreateRoomRootState().copy(isSearchActive = true),
|
||||
aCreateRoomRootState().copy(isSearchActive = true, searchQuery = "someone"),
|
||||
aCreateRoomRootState().copy(
|
||||
isSearchActive = true,
|
||||
searchQuery = "@someone:matrix.org",
|
||||
searchResults = persistentListOf(
|
||||
MatrixUser(id = UserId("@someone:matrix.org")),
|
||||
MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"),
|
||||
MatrixUser(
|
||||
id = UserId("@someone_with_a_very_long_matrix_identifier:a_very_long_domain.org"),
|
||||
username = "hey, I am someone with a very long display name"
|
||||
),
|
||||
MatrixUser(id = UserId("@someone_2:matrix.org"), username = "someone 2"),
|
||||
MatrixUser(id = UserId("@someone_3:matrix.org"), username = "someone 3"),
|
||||
MatrixUser(id = UserId("@someone_4:matrix.org"), username = "someone 4"),
|
||||
MatrixUser(id = UserId("@someone_5:matrix.org"), username = "someone 5"),
|
||||
MatrixUser(id = UserId("@someone_6:matrix.org"), username = "someone 6"),
|
||||
MatrixUser(id = UserId("@someone_7:matrix.org"), username = "someone 7"),
|
||||
MatrixUser(id = UserId("@someone_8:matrix.org"), username = "someone 8"),
|
||||
MatrixUser(id = UserId("@someone_9:matrix.org"), username = "someone 9"),
|
||||
MatrixUser(id = UserId("@someone_10:matrix.org"), username = "someone 10"),
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aCreateRoomRootState() = CreateRoomRootState(
|
||||
eventSink = {}
|
||||
eventSink = {},
|
||||
isSearchActive = false,
|
||||
searchQuery = "",
|
||||
searchResults = persistentListOf(),
|
||||
)
|
||||
|
||||
@@ -22,18 +22,16 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.SearchBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
@@ -45,15 +43,19 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar
|
||||
import io.element.android.libraries.designsystem.theme.components.DockedSearchBar
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
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.SearchBar
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.ui.components.MatrixUserRow
|
||||
import io.element.android.libraries.matrix.ui.model.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import io.element.android.libraries.designsystem.R as DrawableR
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
|
||||
@@ -64,12 +66,10 @@ fun CreateRoomRootView(
|
||||
modifier: Modifier = Modifier,
|
||||
onClosePressed: () -> Unit = {},
|
||||
) {
|
||||
var searchText by rememberSaveable { mutableStateOf("") }
|
||||
var isSearchActive by rememberSaveable { mutableStateOf(false) }
|
||||
Scaffold(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
topBar = {
|
||||
if (!isSearchActive) {
|
||||
if (!state.isSearchActive) {
|
||||
CreateRoomRootViewTopBar(onClosePressed = onClosePressed)
|
||||
}
|
||||
}
|
||||
@@ -80,14 +80,16 @@ fun CreateRoomRootView(
|
||||
) {
|
||||
CreateRoomSearchBar(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = searchText,
|
||||
query = state.searchQuery,
|
||||
placeHolderTitle = stringResource(StringR.string.search_for_someone),
|
||||
active = isSearchActive,
|
||||
onActiveChanged = { isSearchActive = it },
|
||||
onTextChanged = { searchText = it },
|
||||
results = state.searchResults,
|
||||
active = state.isSearchActive,
|
||||
onActiveChanged = { state.eventSink(CreateRoomRootEvents.OnSearchActiveChanged(it)) },
|
||||
onTextChanged = { state.eventSink(CreateRoomRootEvents.UpdateSearchQuery(it)) },
|
||||
onResultSelected = { state.eventSink(CreateRoomRootEvents.StartDM(it)) }
|
||||
)
|
||||
|
||||
if (!isSearchActive) {
|
||||
if (!state.isSearchActive) {
|
||||
CreateRoomActionButtonsList(
|
||||
onNewRoomClicked = { state.eventSink(CreateRoomRootEvents.CreateRoom) },
|
||||
onInvitePeopleClicked = { state.eventSink(CreateRoomRootEvents.InvitePeople) },
|
||||
@@ -123,12 +125,14 @@ fun CreateRoomRootViewTopBar(
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CreateRoomSearchBar(
|
||||
text: String,
|
||||
query: String,
|
||||
placeHolderTitle: String,
|
||||
results: ImmutableList<MatrixUser>,
|
||||
active: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onActiveChanged: (Boolean) -> Unit = {},
|
||||
onTextChanged: (String) -> Unit = {},
|
||||
onResultSelected: (MatrixUser) -> Unit = {},
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
@@ -137,8 +141,8 @@ fun CreateRoomSearchBar(
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
|
||||
DockedSearchBar(
|
||||
query = text,
|
||||
SearchBar(
|
||||
query = query,
|
||||
onQueryChange = onTextChanged,
|
||||
onSearch = { focusManager.clearFocus() },
|
||||
active = active,
|
||||
@@ -153,9 +157,11 @@ fun CreateRoomSearchBar(
|
||||
},
|
||||
leadingIcon = if (active) {
|
||||
{ BackButton(onClick = { onActiveChanged(false) }) }
|
||||
} else null,
|
||||
} else {
|
||||
null
|
||||
},
|
||||
trailingIcon = when {
|
||||
active && text.isNotEmpty() -> {
|
||||
active && query.isNotEmpty() -> {
|
||||
{
|
||||
IconButton(onClick = { onTextChanged("") }) {
|
||||
Icon(Icons.Default.Close, stringResource(StringR.string.a11y_clear))
|
||||
@@ -173,9 +179,17 @@ fun CreateRoomSearchBar(
|
||||
}
|
||||
else -> null
|
||||
},
|
||||
shape = if (!active) SearchBarDefaults.dockedShape else SearchBarDefaults.fullScreenShape,
|
||||
colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent),
|
||||
content = {},
|
||||
content = {
|
||||
LazyColumn {
|
||||
items(results) {
|
||||
CreateRoomSearchResultItem(
|
||||
matrixUser = it,
|
||||
onClick = { onResultSelected(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -199,6 +213,20 @@ fun CreateRoomActionButtonsList(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CreateRoomSearchResultItem(
|
||||
matrixUser: MatrixUser,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
MatrixUserRow(
|
||||
modifier = modifier,
|
||||
matrixUser = matrixUser,
|
||||
avatarSize = AvatarSize.Custom(36.dp),
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CreateRoomActionButton(
|
||||
@DrawableRes iconRes: Int,
|
||||
@@ -209,7 +237,7 @@ fun CreateRoomActionButton(
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 56.dp)
|
||||
.height(56.dp)
|
||||
.clickable { onClick() }
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
|
||||
@@ -22,8 +22,8 @@ import app.cash.molecule.RecompositionClock
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.createroom.impl.root.CreateRoomRootEvents
|
||||
import io.element.android.features.createroom.impl.root.CreateRoomRootPresenter
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.ui.model.MatrixUser
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
@@ -42,7 +42,7 @@ class CreateRoomRootPresenterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - send event`() = runTest {
|
||||
fun `present - trigger action buttons`() = runTest {
|
||||
val presenter = CreateRoomRootPresenter()
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
@@ -52,4 +52,42 @@ class CreateRoomRootPresenterTests {
|
||||
initialState.eventSink(CreateRoomRootEvents.InvitePeople) // Not implemented yet
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - update search query`() = runTest {
|
||||
val presenter = CreateRoomRootPresenter()
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
|
||||
initialState.eventSink(CreateRoomRootEvents.OnSearchActiveChanged(true))
|
||||
assertThat(awaitItem().isSearchActive).isTrue()
|
||||
|
||||
val matrixIdQuery = "@name:matrix.org"
|
||||
initialState.eventSink(CreateRoomRootEvents.UpdateSearchQuery(matrixIdQuery))
|
||||
assertThat(awaitItem().searchQuery).isEqualTo(matrixIdQuery)
|
||||
assertThat(awaitItem().searchResults).containsExactly(MatrixUser(UserId(matrixIdQuery)))
|
||||
|
||||
val notMatrixIdQuery = "name"
|
||||
initialState.eventSink(CreateRoomRootEvents.UpdateSearchQuery(notMatrixIdQuery))
|
||||
assertThat(awaitItem().searchQuery).isEqualTo(notMatrixIdQuery)
|
||||
assertThat(awaitItem().searchResults).isEmpty()
|
||||
|
||||
initialState.eventSink(CreateRoomRootEvents.OnSearchActiveChanged(false))
|
||||
assertThat(awaitItem().isSearchActive).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - trigger start DM action`() = runTest {
|
||||
val presenter = CreateRoomRootPresenter()
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val matrixUser = MatrixUser(UserId("@name:matrix.org"))
|
||||
initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ private fun InitialsAvatar(
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = avatarData.getInitial(),
|
||||
fontSize = (avatarData.size.value / 2).sp,
|
||||
fontSize = (avatarData.size.dp / 2).value.sp,
|
||||
color = Color.White,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,13 +16,16 @@
|
||||
|
||||
package io.element.android.libraries.designsystem.components.avatar
|
||||
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
enum class AvatarSize(val value: Int) {
|
||||
SMALL(32),
|
||||
MEDIUM(40),
|
||||
BIG(48),
|
||||
HUGE(96);
|
||||
sealed class AvatarSize(open val dp: Dp) {
|
||||
|
||||
val dp = value.dp
|
||||
object SMALL : AvatarSize(32.dp)
|
||||
object MEDIUM : AvatarSize(40.dp)
|
||||
object BIG : AvatarSize(48.dp)
|
||||
object HUGE : AvatarSize(96.dp)
|
||||
|
||||
// FIXME maybe remove this field and switch back to an enum (or remove this class) when design system will be integrated
|
||||
data class Custom(override val dp: Dp) : AvatarSize(dp)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package io.element.android.libraries.designsystem.theme.components
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.SearchBarColors
|
||||
import androidx.compose.material3.SearchBarDefaults
|
||||
@@ -34,7 +35,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DockedSearchBar(
|
||||
fun SearchBar(
|
||||
query: String,
|
||||
onQueryChange: (String) -> Unit,
|
||||
onSearch: (String) -> Unit,
|
||||
@@ -45,13 +46,14 @@ fun DockedSearchBar(
|
||||
placeholder: @Composable (() -> Unit)? = null,
|
||||
leadingIcon: @Composable (() -> Unit)? = null,
|
||||
trailingIcon: @Composable (() -> Unit)? = null,
|
||||
shape: Shape = SearchBarDefaults.dockedShape,
|
||||
shape: Shape = SearchBarDefaults.inputFieldShape,
|
||||
colors: SearchBarColors = SearchBarDefaults.colors(),
|
||||
tonalElevation: Dp = SearchBarDefaults.Elevation,
|
||||
windowInsets: WindowInsets = SearchBarDefaults.windowInsets,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
androidx.compose.material3.DockedSearchBar(
|
||||
androidx.compose.material3.SearchBar(
|
||||
query = query,
|
||||
onQueryChange = onQueryChange,
|
||||
onSearch = onSearch,
|
||||
@@ -65,6 +67,7 @@ fun DockedSearchBar(
|
||||
shape = shape,
|
||||
colors = colors,
|
||||
tonalElevation = tonalElevation,
|
||||
windowInsets = windowInsets,
|
||||
interactionSource = interactionSource,
|
||||
content = content,
|
||||
)
|
||||
@@ -80,7 +83,7 @@ internal fun DockedSearchBarDarkPreview() = ElementPreviewDark { ContentToPrevie
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
DockedSearchBar(
|
||||
SearchBar(
|
||||
query = "Some text",
|
||||
onQueryChange = {},
|
||||
onSearch = {},
|
||||
@@ -34,35 +34,34 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.ui.model.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.getBestName
|
||||
|
||||
// FIXME Row are not the same height if there is a name or not.
|
||||
@Composable
|
||||
fun MatrixUserRow(
|
||||
matrixUser: MatrixUser,
|
||||
modifier: Modifier = Modifier,
|
||||
avatarSize: AvatarSize = matrixUser.avatarData.size,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clickable(onClick = onClick)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp)
|
||||
.height(IntrinsicSize.Min),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Avatar(
|
||||
matrixUser.avatarData,
|
||||
matrixUser.avatarData.copy(size = avatarSize),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 12.dp, end = 4.dp, top = 12.dp, bottom = 12.dp)
|
||||
.alignByBaseline()
|
||||
.weight(1f)
|
||||
.padding(start = 12.dp),
|
||||
) {
|
||||
// Name
|
||||
Text(
|
||||
|
||||
@@ -18,7 +18,8 @@ package io.element.android.libraries.matrix.ui.media
|
||||
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.matrix.api.media.MediaResolver
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
fun AvatarData.toMetadata(): MediaResolver.Meta {
|
||||
return MediaResolver.Meta(url = url, kind = MediaResolver.Kind.Thumbnail(size.value))
|
||||
return MediaResolver.Meta(url = url, kind = MediaResolver.Kind.Thumbnail(size.dp.value.roundToInt()))
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user