AwaitRoom : create loading state with placeholders
This commit is contained in:
parent
03b2cbb06f
commit
8667784271
6 changed files with 79 additions and 23 deletions
|
|
@ -14,14 +14,27 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
|
||||||
package io.element.android.appnav
|
package io.element.android.appnav
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.systemBars
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
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
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.bumble.appyx.core.composable.Children
|
import com.bumble.appyx.core.composable.Children
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
|
@ -30,6 +43,7 @@ import com.bumble.appyx.core.node.node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
|
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
|
|
@ -37,10 +51,17 @@ import io.element.android.libraries.architecture.BackstackNode
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
|
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||||
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
|
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.placeholderBackground
|
||||||
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.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
|
@ -86,9 +107,9 @@ class AwaitRoomNode @AssistedInject constructor(
|
||||||
init {
|
init {
|
||||||
roomStateFlow.onEach { room ->
|
roomStateFlow.onEach { room ->
|
||||||
if (room == null) {
|
if (room == null) {
|
||||||
backstack.safeRoot(NavTarget.Loading)
|
backstack.newRoot(NavTarget.Loading)
|
||||||
} else {
|
} else {
|
||||||
backstack.safeRoot(NavTarget.Loaded)
|
backstack.newRoot(NavTarget.Loaded)
|
||||||
}
|
}
|
||||||
}.launchIn(lifecycleScope)
|
}.launchIn(lifecycleScope)
|
||||||
}
|
}
|
||||||
|
|
@ -100,22 +121,57 @@ class AwaitRoomNode @AssistedInject constructor(
|
||||||
val roomFlowNodeCallback = plugins<RoomFlowNode.Callback>()
|
val roomFlowNodeCallback = plugins<RoomFlowNode.Callback>()
|
||||||
val room = roomStateFlow.value
|
val room = roomStateFlow.value
|
||||||
if (room == null) {
|
if (room == null) {
|
||||||
loadingNode(buildContext)
|
loadingNode(buildContext, this::navigateUp)
|
||||||
} else {
|
} else {
|
||||||
val inputs = RoomFlowNode.Inputs(room, initialElement = inputs.initialElement)
|
val inputs = RoomFlowNode.Inputs(room, initialElement = inputs.initialElement)
|
||||||
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback + nodeLifecycleCallbacks)
|
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback + nodeLifecycleCallbacks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NavTarget.Loading -> {
|
NavTarget.Loading -> {
|
||||||
loadingNode(buildContext)
|
loadingNode(buildContext, this::navigateUp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadingNode(buildContext: BuildContext) = node(buildContext) {
|
private fun loadingNode(buildContext: BuildContext, onBackPressed: () -> Unit) = node(buildContext) { modifier ->
|
||||||
Box(modifier = it.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Scaffold(
|
||||||
CircularProgressIndicator()
|
modifier = modifier,
|
||||||
}
|
contentWindowInsets = WindowInsets.systemBars,
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
modifier = Modifier,
|
||||||
|
navigationIcon = {
|
||||||
|
BackButton(onClick = onBackPressed)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Row(
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = { padding ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(padding), contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -267,14 +267,14 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
is NavTarget.Room -> {
|
is NavTarget.Room -> {
|
||||||
val nodeLifecycleCallbacks = plugins<NodeLifecycleCallback>()
|
val nodeLifecycleCallbacks = plugins<NodeLifecycleCallback>()
|
||||||
val callback = object : RoomFlowNode.Callback {
|
val callback = object : RoomFlowNode.Callback {
|
||||||
override fun onForwardedToSingleRoom(roomId: RoomId) {
|
override fun onForwardedToSingleRoom(roomId: RoomId) {
|
||||||
coroutineScope.launch { attachRoom(roomId) }
|
coroutineScope.launch { attachRoom(roomId) }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val inputs = AwaitRoomNode.Inputs(roomId = navTarget.roomId, initialElement = navTarget.initialElement)
|
}
|
||||||
createNode<AwaitRoomNode>(buildContext, plugins = listOf(inputs, callback) + nodeLifecycleCallbacks)
|
val inputs = AwaitRoomNode.Inputs(roomId = navTarget.roomId, initialElement = navTarget.initialElement)
|
||||||
|
createNode<AwaitRoomNode>(buildContext, plugins = listOf(inputs, callback) + nodeLifecycleCallbacks)
|
||||||
}
|
}
|
||||||
NavTarget.Settings -> {
|
NavTarget.Settings -> {
|
||||||
val callback = object : PreferencesEntryPoint.Callback {
|
val callback = object : PreferencesEntryPoint.Callback {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||||
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.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.theme.roomListPlaceholder
|
import io.element.android.libraries.designsystem.theme.placeholderBackground
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -56,7 +56,7 @@ internal fun RoomSummaryPlaceholderRow(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(AvatarSize.RoomListItem.dp)
|
.size(AvatarSize.RoomListItem.dp)
|
||||||
.align(Alignment.CenterVertically)
|
.align(Alignment.CenterVertically)
|
||||||
.background(color = ElementTheme.colors.roomListPlaceholder, shape = CircleShape)
|
.background(color = ElementTheme.colors.placeholderBackground, shape = CircleShape)
|
||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
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.theme.roomListPlaceholder
|
import io.element.android.libraries.designsystem.theme.placeholderBackground
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -37,7 +37,7 @@ fun PlaceholderAtom(
|
||||||
width: Dp,
|
width: Dp,
|
||||||
height: Dp,
|
height: Dp,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
color: Color = ElementTheme.colors.roomListPlaceholder,
|
color: Color = ElementTheme.colors.placeholderBackground,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ fun MaterialTheme.roomListRoomMessageDate() = colorScheme.secondary
|
||||||
val SemanticColors.unreadIndicator
|
val SemanticColors.unreadIndicator
|
||||||
get() = iconAccentTertiary
|
get() = iconAccentTertiary
|
||||||
|
|
||||||
val SemanticColors.roomListPlaceholder
|
val SemanticColors.placeholderBackground
|
||||||
get() = bgSubtleSecondary
|
get() = bgSubtleSecondary
|
||||||
|
|
||||||
// This color is not present in Semantic color, so put hard-coded value for now
|
// This color is not present in Semantic color, so put hard-coded value for now
|
||||||
|
|
@ -83,7 +83,7 @@ private fun ContentToPreview() {
|
||||||
"roomListRoomMessage" to MaterialTheme.roomListRoomMessage(),
|
"roomListRoomMessage" to MaterialTheme.roomListRoomMessage(),
|
||||||
"roomListRoomMessageDate" to MaterialTheme.roomListRoomMessageDate(),
|
"roomListRoomMessageDate" to MaterialTheme.roomListRoomMessageDate(),
|
||||||
"unreadIndicator" to ElementTheme.colors.unreadIndicator,
|
"unreadIndicator" to ElementTheme.colors.unreadIndicator,
|
||||||
"roomListPlaceholder" to ElementTheme.colors.roomListPlaceholder,
|
"placeholderBackground" to ElementTheme.colors.placeholderBackground,
|
||||||
"messageFromMeBackground" to ElementTheme.colors.messageFromMeBackground,
|
"messageFromMeBackground" to ElementTheme.colors.messageFromMeBackground,
|
||||||
"messageFromOtherBackground" to ElementTheme.colors.messageFromOtherBackground,
|
"messageFromOtherBackground" to ElementTheme.colors.messageFromOtherBackground,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||||
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.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.theme.roomListPlaceholder
|
import io.element.android.libraries.designsystem.theme.placeholderBackground
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -53,7 +53,7 @@ fun MatrixUserHeaderPlaceholder(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(vertical = 12.dp)
|
.padding(vertical = 12.dp)
|
||||||
.size(AvatarSize.UserPreference.dp)
|
.size(AvatarSize.UserPreference.dp)
|
||||||
.background(color = ElementTheme.colors.roomListPlaceholder, shape = CircleShape)
|
.background(color = ElementTheme.colors.placeholderBackground, shape = CircleShape)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
Column(
|
Column(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue