Room directory : add tests and cleanup

This commit is contained in:
ganfra 2024-03-28 17:03:34 +01:00
parent 33c3dc07aa
commit 35b12f265f
27 changed files with 573 additions and 77 deletions

View file

@ -28,12 +28,10 @@ import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultRoomDirectoryEntryPoint @Inject constructor() : RoomDirectoryEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomDirectoryEntryPoint.NodeBuilder {
val plugins = ArrayList<Plugin>()
return object : RoomDirectoryEntryPoint.NodeBuilder {
override fun callback(callback: RoomDirectoryEntryPoint.Callback): RoomDirectoryEntryPoint.NodeBuilder {
plugins += callback
return this

View file

@ -35,7 +35,6 @@ class RoomDirectoryNode @AssistedInject constructor(
@Assisted plugins: List<Plugin>,
private val presenter: RoomDirectoryPresenter,
) : Node(buildContext, plugins = plugins) {
private fun onRoomJoined(roomId: RoomId) {
plugins<RoomDirectoryEntryPoint.Callback>().forEach {
it.onOpenRoom(roomId)

View file

@ -26,13 +26,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import io.element.android.features.roomdirectory.impl.root.di.JoinRoom
import io.element.android.features.roomdirectory.impl.root.model.RoomDirectoryListState
import io.element.android.features.roomdirectory.impl.root.model.toFeatureModel
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runUpdatingState
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
@ -46,10 +46,9 @@ import javax.inject.Inject
class RoomDirectoryPresenter @Inject constructor(
private val dispatchers: CoroutineDispatchers,
private val matrixClient: MatrixClient,
private val joinRoom: JoinRoom,
private val roomDirectoryService: RoomDirectoryService,
) : Presenter<RoomDirectoryState> {
@Composable
override fun present(): RoomDirectoryState {
var loadingMore by remember {
@ -68,9 +67,9 @@ class RoomDirectoryPresenter @Inject constructor(
}
LaunchedEffect(searchQuery) {
if (searchQuery == null) return@LaunchedEffect
//debounce search query
// debounce search query
delay(300)
//cancel load more right away
// cancel load more right away
loadingMore = false
roomDirectoryList.filter(searchQuery, 20)
}
@ -108,7 +107,7 @@ class RoomDirectoryPresenter @Inject constructor(
private fun CoroutineScope.joinRoom(state: MutableState<AsyncAction<RoomId>>, roomId: RoomId) = launch {
state.runUpdatingState {
matrixClient.joinRoom(roomId)
joinRoom(roomId)
}
}

View file

@ -25,39 +25,14 @@ import io.element.android.libraries.matrix.api.core.RoomId
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirectoryState> {
open class RoomDirectoryStateProvider : PreviewParameterProvider<RoomDirectoryState> {
override val values: Sequence<RoomDirectoryState>
get() = sequenceOf(
aRoomDirectoryState(),
aRoomDirectoryState(
query = "Element",
roomDescriptions = persistentListOf(
RoomDescription(
roomId = RoomId("@exa:matrix.org"),
name = "Element X Android",
description = "Element X is a secure, private and decentralized messenger.",
avatarData = AvatarData(
id = "@exa:matrix.org",
name = "Element X Android",
url = null,
size = AvatarSize.RoomDirectoryItem
),
canBeJoined = true,
),
RoomDescription(
roomId = RoomId("@exi:matrix.org"),
name = "Element X iOS",
description = "Element X is a secure, private and decentralized messenger.",
avatarData = AvatarData(
id = "@exi:matrix.org",
name = "Element X iOS",
url = null,
size = AvatarSize.RoomDirectoryItem
),
canBeJoined = false,
)
)
),
roomDescriptions = aRoomDescriptionList(),
)
)
}
@ -66,10 +41,40 @@ fun aRoomDirectoryState(
displayLoadMoreIndicator: Boolean = false,
roomDescriptions: ImmutableList<RoomDescription> = persistentListOf(),
joinRoomAction: AsyncAction<RoomId> = AsyncAction.Uninitialized,
eventSink: (RoomDirectoryEvents) -> Unit = {},
) = RoomDirectoryState(
query = query,
roomDescriptions = roomDescriptions,
displayLoadMoreIndicator = displayLoadMoreIndicator,
joinRoomAction = joinRoomAction,
eventSink = {},
eventSink = eventSink,
)
fun aRoomDescriptionList(): ImmutableList<RoomDescription> {
return persistentListOf(
RoomDescription(
roomId = RoomId("!exa:matrix.org"),
name = "Element X Android",
description = "Element X is a secure, private and decentralized messenger.",
avatarData = AvatarData(
id = "!exa:matrix.org",
name = "Element X Android",
url = null,
size = AvatarSize.RoomDirectoryItem
),
canBeJoined = true,
),
RoomDescription(
roomId = RoomId("!exi:matrix.org"),
name = "Element X iOS",
description = "Element X is a secure, private and decentralized messenger.",
avatarData = AvatarData(
id = "!exi:matrix.org",
name = "Element X iOS",
url = null,
size = AvatarSize.RoomDirectoryItem
),
canBeJoined = false,
)
)
}

View file

@ -40,6 +40,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -61,6 +62,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
@ -71,7 +73,6 @@ fun RoomDirectoryView(
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
) {
fun joinRoom(roomId: RoomId) {
state.eventSink(RoomDirectoryEvents.JoinRoom(roomId))
}
@ -86,8 +87,8 @@ fun RoomDirectoryView(
state = state,
onResultClicked = ::joinRoom,
modifier = Modifier
.padding(padding)
.consumeWindowInsets(padding)
.padding(padding)
.consumeWindowInsets(padding)
)
}
)
@ -96,7 +97,8 @@ fun RoomDirectoryView(
onSuccess = onRoomJoined,
onErrorDismiss = {
state.eventSink(RoomDirectoryEvents.JoinRoomDismissError)
})
}
)
}
@OptIn(ExperimentalMaterial3Api::class)
@ -171,7 +173,7 @@ private fun RoomDirectoryRoomList(
if (displayLoadMoreIndicator) {
item {
LoadMoreIndicator(modifier = Modifier.fillMaxWidth())
LaunchedEffect(Unit) {
LaunchedEffect(onReachedLoadMore) {
onReachedLoadMore()
}
}
@ -182,10 +184,10 @@ private fun RoomDirectoryRoomList(
@Composable
private fun LoadMoreIndicator(modifier: Modifier = Modifier) {
Box(
modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(24.dp),
modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(24.dp),
contentAlignment = Alignment.Center,
) {
CircularProgressIndicator(
@ -213,7 +215,7 @@ private fun SearchTextField(
) {
val focusManager = LocalFocusManager.current
TextField(
modifier = modifier,
modifier = modifier.testTag(TestTags.searchTextField.value),
textStyle = ElementTheme.typography.fontBodyLgRegular,
singleLine = true,
value = query,
@ -255,14 +257,14 @@ private fun RoomDirectoryRoomRow(
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable { onClick(roomDescription.roomId) }
.padding(
top = 12.dp,
bottom = 12.dp,
start = 16.dp,
)
.height(IntrinsicSize.Min),
.fillMaxWidth()
.clickable { onClick(roomDescription.roomId) }
.padding(
top = 12.dp,
bottom = 12.dp,
start = 16.dp,
)
.height(IntrinsicSize.Min),
) {
Avatar(
avatarData = roomDescription.avatarData,
@ -270,8 +272,8 @@ private fun RoomDirectoryRoomRow(
)
Column(
modifier = Modifier
.weight(1f)
.padding(start = 16.dp)
.weight(1f)
.padding(start = 16.dp)
) {
Text(
text = roomDescription.name,
@ -293,8 +295,8 @@ private fun RoomDirectoryRoomRow(
text = stringResource(id = CommonStrings.action_join),
color = ElementTheme.colors.textSuccessPrimary,
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 4.dp, end = 12.dp)
.align(Alignment.CenterVertically)
.padding(start = 4.dp, end = 12.dp)
)
} else {
Spacer(modifier = Modifier.width(24.dp))
@ -304,7 +306,7 @@ private fun RoomDirectoryRoomRow(
@PreviewsDayNight
@Composable
fun RoomDirectorySearchViewLightPreview(@PreviewParameter(RoomDirectorySearchStateProvider::class) state: RoomDirectoryState) = ElementPreview {
internal fun RoomDirectoryViewPreview(@PreviewParameter(RoomDirectoryStateProvider::class) state: RoomDirectoryState) = ElementPreview {
RoomDirectoryView(
state = state,
onRoomJoined = {},

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 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.roomdirectory.impl.root.di
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import javax.inject.Inject
interface JoinRoom {
suspend operator fun invoke(roomId: RoomId): Result<RoomId>
}
@ContributesBinding(SessionScope::class)
class DefaultJoinRoom @Inject constructor(private val client: MatrixClient) : JoinRoom {
override suspend fun invoke(roomId: RoomId) = client.joinRoom(roomId)
}

View file

@ -24,7 +24,6 @@ internal data class RoomDirectoryListState(
val hasMoreToLoad: Boolean,
val items: ImmutableList<RoomDescription>,
) {
companion object {
val Default = RoomDirectoryListState(
hasMoreToLoad = true,