Room: avoid calling displayName/avatarData on each recomposition
This commit is contained in:
parent
c0b8388fad
commit
2f92203d85
6 changed files with 99 additions and 50 deletions
|
|
@ -16,19 +16,13 @@
|
||||||
|
|
||||||
package io.element.android.appnav.room
|
package io.element.android.appnav.room
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
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.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
|
@ -37,9 +31,8 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
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 androidx.compose.ui.unit.sp
|
|
||||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
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.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
|
|
@ -48,7 +41,6 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre
|
||||||
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.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.theme.placeholderBackground
|
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
|
@ -103,20 +95,7 @@ private fun LoadingRoomTopBar(
|
||||||
BackButton(onClick = onBackClicked)
|
BackButton(onClick = onBackClicked)
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
Row(
|
IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp)
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(AvatarSize.TimelineRoom.dp)
|
|
||||||
.align(Alignment.CenterVertically)
|
|
||||||
.background(color = ElementTheme.colors.placeholderBackground, shape = CircleShape)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
PlaceholderAtom(width = 20.dp, height = 7.dp)
|
|
||||||
Spacer(modifier = Modifier.width(7.dp))
|
|
||||||
PlaceholderAtom(width = 45.dp, height = 7.dp)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
windowInsets = WindowInsets(0.dp),
|
windowInsets = WindowInsets(0.dp),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.produceState
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
|
@ -76,6 +75,7 @@ import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
|
||||||
import io.element.android.libraries.textcomposer.MessageComposerMode
|
import io.element.android.libraries.textcomposer.MessageComposerMode
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class MessagesPresenter @AssistedInject constructor(
|
class MessagesPresenter @AssistedInject constructor(
|
||||||
|
|
@ -109,11 +109,13 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||||
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
||||||
val roomName by produceState(initialValue = room.displayName, key1 = syncUpdateFlow.value) {
|
var roomName: Async<String> by remember { mutableStateOf(Async.Uninitialized) }
|
||||||
value = room.displayName
|
var roomAvatar: Async<AvatarData> by remember { mutableStateOf(Async.Uninitialized) }
|
||||||
}
|
LaunchedEffect(syncUpdateFlow.value) {
|
||||||
val roomAvatar by produceState(initialValue = room.avatarData(), key1 = syncUpdateFlow.value) {
|
withContext(dispatchers.io) {
|
||||||
value = room.avatarData()
|
roomName = Async.Success(room.displayName)
|
||||||
|
roomAvatar = Async.Success(room.avatarData())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var hasDismissedInviteDialog by rememberSaveable {
|
var hasDismissedInviteDialog by rememberSaveable {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
@Immutable
|
@Immutable
|
||||||
data class MessagesState(
|
data class MessagesState(
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
val roomName: String,
|
val roomName: Async<String>,
|
||||||
val roomAvatar: AvatarData,
|
val roomAvatar: Async<AvatarData>,
|
||||||
val userHasPermissionToSendMessage: Boolean,
|
val userHasPermissionToSendMessage: Boolean,
|
||||||
val composerState: MessageComposerState,
|
val composerState: MessageComposerState,
|
||||||
val timelineState: TimelineState,
|
val timelineState: TimelineState,
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
||||||
override val values: Sequence<MessagesState>
|
override val values: Sequence<MessagesState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aMessagesState(),
|
aMessagesState(),
|
||||||
|
aMessagesState().copy(
|
||||||
|
roomName = Async.Uninitialized,
|
||||||
|
roomAvatar = Async.Uninitialized,
|
||||||
|
),
|
||||||
aMessagesState().copy(hasNetworkConnection = false),
|
aMessagesState().copy(hasNetworkConnection = false),
|
||||||
aMessagesState().copy(composerState = aMessageComposerState().copy(showAttachmentSourcePicker = true)),
|
aMessagesState().copy(composerState = aMessageComposerState().copy(showAttachmentSourcePicker = true)),
|
||||||
aMessagesState().copy(userHasPermissionToSendMessage = false),
|
aMessagesState().copy(userHasPermissionToSendMessage = false),
|
||||||
|
|
@ -43,8 +47,8 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
||||||
|
|
||||||
fun aMessagesState() = MessagesState(
|
fun aMessagesState() = MessagesState(
|
||||||
roomId = RoomId("!id:domain"),
|
roomId = RoomId("!id:domain"),
|
||||||
roomName = "Room name",
|
roomName = Async.Success("Room name"),
|
||||||
roomAvatar = AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom),
|
roomAvatar = Async.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)),
|
||||||
userHasPermissionToSendMessage = true,
|
userHasPermissionToSendMessage = true,
|
||||||
composerState = aMessageComposerState().copy(
|
composerState = aMessageComposerState().copy(
|
||||||
text = "Hello",
|
text = "Hello",
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,11 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
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.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||||
import io.element.android.features.messages.impl.actionlist.ActionListView
|
import io.element.android.features.messages.impl.actionlist.ActionListView
|
||||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||||
|
|
@ -64,10 +62,12 @@ import io.element.android.features.messages.impl.timeline.components.retrysendme
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||||
import io.element.android.libraries.androidutils.ui.hideKeyboard
|
import io.element.android.libraries.androidutils.ui.hideKeyboard
|
||||||
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
|
||||||
import io.element.android.libraries.designsystem.components.ProgressDialog
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
import io.element.android.libraries.designsystem.components.ProgressDialogType
|
import io.element.android.libraries.designsystem.components.ProgressDialogType
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
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.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
|
|
@ -137,8 +137,8 @@ fun MessagesView(
|
||||||
Column {
|
Column {
|
||||||
ConnectivityIndicatorView(isOnline = state.hasNetworkConnection)
|
ConnectivityIndicatorView(isOnline = state.hasNetworkConnection)
|
||||||
MessagesViewTopBar(
|
MessagesViewTopBar(
|
||||||
roomTitle = state.roomName,
|
roomName = state.roomName.dataOrNull(),
|
||||||
roomAvatar = state.roomAvatar,
|
roomAvatar = state.roomAvatar.dataOrNull(),
|
||||||
onBackPressed = onBackPressed,
|
onBackPressed = onBackPressed,
|
||||||
onRoomDetailsClicked = onRoomDetailsClicked,
|
onRoomDetailsClicked = onRoomDetailsClicked,
|
||||||
)
|
)
|
||||||
|
|
@ -289,29 +289,50 @@ fun MessagesViewContent(
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MessagesViewTopBar(
|
fun MessagesViewTopBar(
|
||||||
roomTitle: String,
|
roomName: String?,
|
||||||
roomAvatar: AvatarData,
|
roomAvatar: AvatarData?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onRoomDetailsClicked: () -> Unit = {},
|
onRoomDetailsClicked: () -> Unit = {},
|
||||||
onBackPressed: () -> Unit = {},
|
onBackPressed: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
@Composable
|
||||||
|
fun RoomAvatarAndNameRow(
|
||||||
|
roomName: String,
|
||||||
|
roomAvatar: AvatarData,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Avatar(roomAvatar)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = roomName,
|
||||||
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
BackButton(onClick = onBackPressed)
|
BackButton(onClick = onBackPressed)
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
Row(
|
val titleModifier = Modifier.clickable { onRoomDetailsClicked() }
|
||||||
modifier = Modifier.clickable { onRoomDetailsClicked() },
|
if (roomName != null && roomAvatar != null) {
|
||||||
verticalAlignment = Alignment.CenterVertically
|
RoomAvatarAndNameRow(
|
||||||
) {
|
roomName = roomName,
|
||||||
Avatar(roomAvatar)
|
roomAvatar = roomAvatar,
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
modifier = titleModifier
|
||||||
Text(
|
)
|
||||||
text = roomTitle,
|
} else {
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
IconTitlePlaceholdersRowMolecule(
|
||||||
maxLines = 1,
|
iconSize = AvatarSize.TimelineRoom.dp,
|
||||||
overflow = TextOverflow.Ellipsis
|
modifier = titleModifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package io.element.android.libraries.designsystem.atomic.molecules
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||||
|
import io.element.android.libraries.designsystem.theme.placeholderBackground
|
||||||
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun IconTitlePlaceholdersRowMolecule(
|
||||||
|
iconSize: Dp,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
|
||||||
|
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalArrangement = horizontalArrangement,
|
||||||
|
verticalAlignment = verticalAlignment,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(iconSize)
|
||||||
|
.align(Alignment.CenterVertically)
|
||||||
|
.background(color = ElementTheme.colors.placeholderBackground, shape = CircleShape)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
PlaceholderAtom(width = 20.dp, height = 7.dp)
|
||||||
|
Spacer(modifier = Modifier.width(7.dp))
|
||||||
|
PlaceholderAtom(width = 45.dp, height = 7.dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue