Add an empty state for the space screen if the user can modify its graph (#6064)
* Add an empty state for the space screen if the user can modify its graph. It adds a new 'create room' button that allows you to open the create room screen with some preset values. * When computing the editable spaces in `ConfigureRoomPresenter`, also set up the initial selected parent space if possible * Use `Builder` pattern for `CreateRoomEntryPoint` * Update screenshots --------- Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
committed by
GitHub
parent
cc4bf95cac
commit
4b4492681b
@@ -15,12 +15,13 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
interface CreateRoomEntryPoint : FeatureEntryPoint {
|
||||
fun createNode(
|
||||
isSpace: Boolean,
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: Callback,
|
||||
): Node
|
||||
interface Builder {
|
||||
fun setIsSpace(isSpace: Boolean): Builder
|
||||
fun setParentSpace(parentSpaceId: RoomId): Builder
|
||||
fun build(): Node
|
||||
}
|
||||
|
||||
fun builder(parentNode: Node, buildContext: BuildContext, callback: Callback): Builder
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onRoomCreated(roomId: RoomId)
|
||||
|
||||
@@ -38,7 +38,7 @@ class CreateRoomFlowNode(
|
||||
@Assisted plugins: List<Plugin>,
|
||||
) : BaseFlowNode<CreateRoomFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.ConfigureRoom(isSpace = plugins.filterIsInstance<Inputs>().first().isSpace),
|
||||
initialElement = initialElementFromInputs(plugins.filterIsInstance<Inputs>().first()),
|
||||
savedStateMap = buildContext.savedStateMap,
|
||||
),
|
||||
buildContext = buildContext,
|
||||
@@ -46,7 +46,8 @@ class CreateRoomFlowNode(
|
||||
) {
|
||||
@Parcelize
|
||||
data class Inputs(
|
||||
val isSpace: Boolean
|
||||
val isSpace: Boolean,
|
||||
val parentSpaceId: RoomId?,
|
||||
) : NodeInputs, Parcelable
|
||||
|
||||
private val callback: CreateRoomEntryPoint.Callback = callback()
|
||||
@@ -54,7 +55,7 @@ class CreateRoomFlowNode(
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
is NavTarget.ConfigureRoom -> {
|
||||
val inputs = ConfigureRoomNode.Inputs(isSpace = navTarget.isSpace)
|
||||
val inputs = ConfigureRoomNode.Inputs(isSpace = navTarget.isSpace, parentSpaceId = navTarget.parentSpaceId)
|
||||
val callback = object : ConfigureRoomNode.Callback {
|
||||
override fun onCreateRoomSuccess(roomId: RoomId) {
|
||||
backstack.replace(NavTarget.AddPeople(roomId))
|
||||
@@ -81,9 +82,14 @@ class CreateRoomFlowNode(
|
||||
|
||||
sealed interface NavTarget : Parcelable {
|
||||
@Parcelize
|
||||
data class ConfigureRoom(val isSpace: Boolean) : NavTarget
|
||||
data class ConfigureRoom(val isSpace: Boolean, val parentSpaceId: RoomId?) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class AddPeople(val roomId: RoomId) : NavTarget
|
||||
}
|
||||
}
|
||||
|
||||
private fun initialElementFromInputs(inputs: CreateRoomFlowNode.Inputs) = CreateRoomFlowNode.NavTarget.ConfigureRoom(
|
||||
isSpace = inputs.isSpace,
|
||||
parentSpaceId = inputs.parentSpaceId,
|
||||
)
|
||||
|
||||
@@ -14,16 +14,35 @@ import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultCreateRoomEntryPoint : CreateRoomEntryPoint {
|
||||
override fun createNode(
|
||||
isSpace: Boolean,
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: CreateRoomEntryPoint.Callback,
|
||||
): Node {
|
||||
val inputs = CreateRoomFlowNode.Inputs(isSpace)
|
||||
return parentNode.createNode<CreateRoomFlowNode>(buildContext, listOf(inputs, callback))
|
||||
class Builder(
|
||||
private val parentNode: Node,
|
||||
private val buildContext: BuildContext,
|
||||
private val callback: CreateRoomEntryPoint.Callback,
|
||||
) : CreateRoomEntryPoint.Builder {
|
||||
private var isSpace = false
|
||||
private var parentSpaceId: RoomId? = null
|
||||
|
||||
override fun setIsSpace(isSpace: Boolean): Builder {
|
||||
this.isSpace = isSpace
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setParentSpace(parentSpaceId: RoomId): Builder {
|
||||
this.parentSpaceId = parentSpaceId
|
||||
return this
|
||||
}
|
||||
|
||||
override fun build(): Node {
|
||||
val inputs = CreateRoomFlowNode.Inputs(isSpace = isSpace, parentSpaceId = parentSpaceId)
|
||||
return parentNode.createNode<CreateRoomFlowNode>(buildContext, listOf(inputs, callback))
|
||||
}
|
||||
}
|
||||
|
||||
override fun builder(parentNode: Node, buildContext: BuildContext, callback: CreateRoomEntryPoint.Callback): CreateRoomEntryPoint.Builder {
|
||||
return Builder(parentNode, buildContext, callback)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,11 +42,12 @@ class ConfigureRoomNode(
|
||||
@Parcelize
|
||||
data class Inputs(
|
||||
val isSpace: Boolean,
|
||||
val parentSpaceId: RoomId?,
|
||||
) : NodeInputs, Parcelable
|
||||
|
||||
private val inputs = inputs<Inputs>()
|
||||
|
||||
private val presenter = presenterFactory.create(inputs.isSpace)
|
||||
private val presenter = presenterFactory.create(inputs.isSpace, inputs.parentSpaceId)
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
|
||||
@@ -63,6 +63,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
@AssistedInject
|
||||
class ConfigureRoomPresenter(
|
||||
@Assisted private val isSpace: Boolean,
|
||||
@Assisted private val initialParentSpaceId: RoomId?,
|
||||
private val dataStore: CreateRoomConfigStore,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val mediaPickerProvider: PickerProvider,
|
||||
@@ -75,7 +76,7 @@ class ConfigureRoomPresenter(
|
||||
) : Presenter<ConfigureRoomState> {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(isSpace: Boolean): ConfigureRoomPresenter
|
||||
fun create(isSpace: Boolean, parentSpaceId: RoomId?): ConfigureRoomPresenter
|
||||
}
|
||||
|
||||
private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
|
||||
@@ -122,6 +123,9 @@ class ConfigureRoomPresenter(
|
||||
} else {
|
||||
persistentListOf()
|
||||
}
|
||||
|
||||
val parentSpace = spaces.find { it.roomId == initialParentSpaceId }
|
||||
parentSpace?.let { dataStore.setParentSpace(it) }
|
||||
}
|
||||
|
||||
LaunchedEffect(cameraPermissionState.permissionGranted) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
<string name="screen_create_room_name_placeholder">"Add name…"</string>
|
||||
<string name="screen_create_room_new_room_title">"New room"</string>
|
||||
<string name="screen_create_room_new_space_title">"New space"</string>
|
||||
<string name="screen_create_room_parent_space_home_description">"(no space)"</string>
|
||||
<string name="screen_create_room_parent_space_home_title">"Home"</string>
|
||||
<string name="screen_create_room_private_option_description">"Only people invited can join."</string>
|
||||
<string name="screen_create_room_private_option_title">"Private"</string>
|
||||
<string name="screen_create_room_public_option_description">"Anyone can find this room.
|
||||
|
||||
@@ -532,6 +532,7 @@ class ConfigureRoomPresenterTest {
|
||||
|
||||
private fun createConfigureRoomPresenter(
|
||||
isSpace: Boolean = false,
|
||||
initialParenSpaceId: RoomId? = null,
|
||||
roomAliasHelper: RoomAliasHelper = FakeRoomAliasHelper(),
|
||||
dataStore: CreateRoomConfigStore = CreateRoomConfigStore(roomAliasHelper),
|
||||
matrixClient: MatrixClient = createMatrixClient(),
|
||||
@@ -543,6 +544,7 @@ class ConfigureRoomPresenterTest {
|
||||
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
|
||||
) = ConfigureRoomPresenter(
|
||||
isSpace = isSpace,
|
||||
initialParentSpaceId = initialParenSpaceId,
|
||||
dataStore = dataStore,
|
||||
matrixClient = matrixClient,
|
||||
mediaPickerProvider = pickerProvider,
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
import org.junit.Rule
|
||||
@@ -36,15 +37,16 @@ class DefaultCreateRoomEntryPointTest {
|
||||
plugins = plugins,
|
||||
)
|
||||
}
|
||||
val buildContext = BuildContext.root(null)
|
||||
|
||||
val callback = object : CreateRoomEntryPoint.Callback {
|
||||
override fun onRoomCreated(roomId: RoomId) = lambdaError()
|
||||
}
|
||||
val result = entryPoint.createNode(
|
||||
isSpace = false,
|
||||
parentNode = parentNode,
|
||||
buildContext = BuildContext.root(null),
|
||||
callback = callback,
|
||||
)
|
||||
val result = entryPoint
|
||||
.builder(parentNode, buildContext, callback)
|
||||
.setIsSpace(true)
|
||||
.setParentSpace(A_ROOM_ID)
|
||||
.build()
|
||||
assertThat(result.plugins).contains(callback)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ android {
|
||||
dependencies {
|
||||
implementation(projects.features.createroom.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.tests.testutils)
|
||||
}
|
||||
|
||||
@@ -10,13 +10,19 @@ package io.element.android.features.createroom.api
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeCreateRoomEntryPoint : CreateRoomEntryPoint {
|
||||
override fun createNode(
|
||||
isSpace: Boolean,
|
||||
class Builder : CreateRoomEntryPoint.Builder {
|
||||
override fun setIsSpace(isSpace: Boolean): Builder = this
|
||||
override fun setParentSpace(parentSpaceId: RoomId): Builder = this
|
||||
override fun build(): Node = lambdaError()
|
||||
}
|
||||
|
||||
override fun builder(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: CreateRoomEntryPoint.Callback,
|
||||
): Node = lambdaError()
|
||||
): Builder = lambdaError()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user