Better understanding of the model: server / protocol

Iso Element Web on this part.
This commit is contained in:
Benoit Marty 2021-05-26 17:38:35 +02:00
parent 64222ff704
commit 4641f77842
8 changed files with 263 additions and 91 deletions

View File

@ -17,10 +17,9 @@
package im.vector.app.features.roomdirectory
/**
* This class describes a rooms directory server.
* This class describes a rooms directory server protocol.
*/
data class RoomDirectoryData(
/**
* The server name (might be null)
* Set null when the server is the current user's home server.
@ -30,7 +29,12 @@ data class RoomDirectoryData(
/**
* The display name (the server description)
*/
val displayName: String = DEFAULT_HOME_SERVER_NAME,
val displayName: String = MATRIX_PROTOCOL_NAME,
/**
* the avatar url
*/
val avatarUrl: String? = null,
/**
* The third party server identifier
@ -40,15 +44,10 @@ data class RoomDirectoryData(
/**
* Tell if all the federated servers must be included
*/
val includeAllNetworks: Boolean = false,
/**
* the avatar url
*/
val avatarUrl: String? = null
val includeAllNetworks: Boolean = false
) {
companion object {
const val DEFAULT_HOME_SERVER_NAME = "Matrix"
const val MATRIX_PROTOCOL_NAME = "Matrix"
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2021 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 im.vector.app.features.roomdirectory
data class RoomDirectoryServer(
val serverName: String,
/**
* True if this is the current user server
*/
val isUserServer: Boolean,
/**
* Supported protocols
* TODO Rename RoomDirectoryData to RoomDirectoryProtocols
*/
val protocols: List<RoomDirectoryData>
)

View File

@ -229,9 +229,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(
Timber.w("Try to join an already joining room. Should not happen")
return@withState
}
val viaServers = state.roomDirectoryData.homeServer
?.let { listOf(it) }
.orEmpty()
val viaServers = listOfNotNull(state.roomDirectoryData.homeServer)
viewModelScope.launch {
try {
session.joinRoom(action.roomId, viaServers = viaServers)

View File

@ -19,54 +19,88 @@ package im.vector.app.features.roomdirectory.picker
import im.vector.app.R
import im.vector.app.core.resources.StringArrayProvider
import im.vector.app.features.roomdirectory.RoomDirectoryData
import im.vector.app.features.roomdirectory.RoomDirectoryServer
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
import javax.inject.Inject
class RoomDirectoryListCreator @Inject constructor(private val stringArrayProvider: StringArrayProvider,
private val session: Session) {
class RoomDirectoryListCreator @Inject constructor(
private val stringArrayProvider: StringArrayProvider,
private val session: Session
) {
fun computeDirectories(thirdPartyProtocolData: Map<String, ThirdPartyProtocol>): List<RoomDirectoryData> {
val result = ArrayList<RoomDirectoryData>()
fun computeDirectories(thirdPartyProtocolData: Map<String, ThirdPartyProtocol>): List<RoomDirectoryServer> {
val result = ArrayList<RoomDirectoryServer>()
val protocols = ArrayList<RoomDirectoryData>()
// Add user homeserver name
val userHsName = session.myUserId.substringAfter(":")
result.add(RoomDirectoryData(
displayName = userHsName,
includeAllNetworks = true
))
// Add user's HS but for Matrix public rooms only
result.add(RoomDirectoryData())
// Add custom directory servers
val hsNamesList = stringArrayProvider.getStringArray(R.array.room_directory_servers)
hsNamesList.forEach {
if (it != userHsName) {
// Use the server name as a default display name
result.add(RoomDirectoryData(
homeServer = it,
displayName = it,
includeAllNetworks = true
))
}
}
// Add default protocol
protocols.add(
RoomDirectoryData(
homeServer = null,
displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME,
includeAllNetworks = false
)
)
// Add result of the request
thirdPartyProtocolData.forEach {
it.value.instances?.forEach { thirdPartyProtocolInstance ->
result.add(RoomDirectoryData(
homeServer = null,
displayName = thirdPartyProtocolInstance.desc ?: "",
thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId,
includeAllNetworks = false,
// Default to protocol icon
avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon
))
protocols.add(
RoomDirectoryData(
homeServer = null,
displayName = thirdPartyProtocolInstance.desc ?: "",
thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId,
includeAllNetworks = false,
// Default to protocol icon
avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon
)
)
}
}
// Add all rooms
protocols.add(
RoomDirectoryData(
homeServer = null,
displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME,
includeAllNetworks = true
)
)
result.add(
RoomDirectoryServer(
serverName = userHsName,
isUserServer = true,
protocols = protocols
)
)
// Add custom directory servers, form the config file, excluding the current user homeserver
stringArrayProvider.getStringArray(R.array.room_directory_servers)
.filter { it != userHsName }
.forEach {
// Use the server name as a default display name
result.add(
RoomDirectoryServer(
serverName = it,
isUserServer = false,
protocols = listOf(
RoomDirectoryData(
homeServer = it,
displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME,
includeAllNetworks = false
)
)
)
)
}
// TODO Add manually added server by the user
return result
}
}

View File

@ -21,33 +21,38 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success
import im.vector.app.R
import im.vector.app.core.epoxy.dividerItem
import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.join
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.roomdirectory.RoomDirectoryData
import im.vector.app.features.roomdirectory.RoomDirectoryServer
import javax.inject.Inject
class RoomDirectoryPickerController @Inject constructor(private val stringProvider: StringProvider,
private val errorFormatter: ErrorFormatter,
private val roomDirectoryListCreator: RoomDirectoryListCreator
class RoomDirectoryPickerController @Inject constructor(
private val stringProvider: StringProvider,
colorProvider: ColorProvider,
private val errorFormatter: ErrorFormatter,
private val roomDirectoryListCreator: RoomDirectoryListCreator
) : TypedEpoxyController<RoomDirectoryPickerViewState>() {
var callback: Callback? = null
var index = 0
private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
override fun buildModels(viewState: RoomDirectoryPickerViewState) {
val host = this
val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest
when (asyncThirdPartyProtocol) {
when (val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest) {
is Success -> {
val directories = roomDirectoryListCreator.computeDirectories(asyncThirdPartyProtocol())
directories.forEach {
buildDirectory(it)
}
directories.join(
each = { _, roomDirectoryServer -> buildDirectory(roomDirectoryServer) },
between = { idx, _ -> buildDivider(idx) }
)
}
is Incomplete -> {
loadingItem {
@ -64,28 +69,46 @@ class RoomDirectoryPickerController @Inject constructor(private val stringProvid
}
}
private fun buildDirectory(roomDirectoryData: RoomDirectoryData) {
private fun buildDivider(idx: Int) {
val host = this
roomDirectoryItem {
id(host.index++)
dividerItem {
id("divider_${idx}")
color(host.dividerColor)
}
}
directoryName(roomDirectoryData.displayName)
private fun buildDirectory(roomDirectoryServer: RoomDirectoryServer) {
val host = this
roomDirectoryServerItem {
id("server_" + roomDirectoryServer.serverName)
serverName(roomDirectoryServer.serverName)
val description = when {
roomDirectoryData.includeAllNetworks ->
host.stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryData.displayName)
"Matrix" == roomDirectoryData.displayName ->
host.stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryData.displayName)
else ->
null
if (roomDirectoryServer.isUserServer) {
serverDescription(host.stringProvider.getString(R.string.directory_your_server))
}
}
directoryDescription(description)
directoryAvatarUrl(roomDirectoryData.avatarUrl)
includeAllNetworks(roomDirectoryData.includeAllNetworks)
roomDirectoryServer.protocols.forEach { roomDirectoryData ->
roomDirectoryItem {
id("server_" + roomDirectoryServer.serverName + "_proto_" + roomDirectoryData.displayName)
directoryName(
if (roomDirectoryData.includeAllNetworks) {
host.stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryServer.serverName)
} else {
roomDirectoryData.displayName
}
)
if (roomDirectoryData.displayName == RoomDirectoryData.MATRIX_PROTOCOL_NAME && !roomDirectoryData.includeAllNetworks) {
directoryDescription(
host.stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryServer.serverName)
)
}
directoryAvatarUrl(roomDirectoryData.avatarUrl)
includeAllNetworks(roomDirectoryData.includeAllNetworks)
globalListener {
host.callback?.onRoomDirectoryClicked(roomDirectoryData)
globalListener {
host.callback?.onRoomDirectoryClicked(roomDirectoryData)
}
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2021 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 im.vector.app.features.roomdirectory.picker
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_room_directory_server)
abstract class RoomDirectoryServerItem : VectorEpoxyModel<RoomDirectoryServerItem.Holder>() {
@EpoxyAttribute
var serverName: String? = null
@EpoxyAttribute
var serverDescription: String? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.nameView.text = serverName
holder.descriptionView.setTextOrHide(serverDescription)
}
class Holder : VectorEpoxyHolder() {
val nameView by bind<TextView>(R.id.itemRoomDirectoryServerName)
val descriptionView by bind<TextView>(R.id.itemRoomDirectoryServerDescription)
}
}

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@ -15,23 +14,23 @@
android:id="@+id/itemRoomDirectoryAvatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:background="@drawable/circle"
android:contentDescription="@string/avatar"
android:padding="8dp"
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
tools:src="@drawable/network_matrix" />
<TextView
android:id="@+id/itemRoomDirectoryName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:ellipsize="end"
android:lines="1"
android:maxLines="2"
@ -49,26 +48,19 @@
android:id="@+id/itemRoomDirectoryDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:ellipsize="end"
android:lines="1"
android:maxLines="2"
android:textColor="?riotx_text_primary"
android:textSize="15sp"
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/itemRoomDirectoryAvatar"
app:layout_constraintTop_toBottomOf="@id/itemRoomDirectoryName"
tools:text="@string/directory_server_all_rooms_on_server" />
<View
android:id="@+id/itemRoomDirectoryBottomSeparator"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?riotx_header_panel_border_mobile"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
tools:text="@string/directory_server_native_rooms"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?vctr_list_header_background_color"
android:minHeight="?listPreferredItemHeight">
<TextView
android:id="@+id/itemRoomDirectoryServerName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:ellipsize="end"
android:lines="1"
android:maxLines="2"
android:textColor="?riotx_text_primary"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryServerDescription"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/itemRoomDirectoryServerDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:ellipsize="end"
android:lines="1"
android:maxLines="2"
android:textColor="?riotx_text_secondary"
android:textSize="15sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/itemRoomDirectoryServerName"
tools:text="@string/directory_your_server"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>