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 package im.vector.app.features.roomdirectory
/** /**
* This class describes a rooms directory server. * This class describes a rooms directory server protocol.
*/ */
data class RoomDirectoryData( data class RoomDirectoryData(
/** /**
* The server name (might be null) * The server name (might be null)
* Set null when the server is the current user's home server. * 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) * 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 * The third party server identifier
@ -40,15 +44,10 @@ data class RoomDirectoryData(
/** /**
* Tell if all the federated servers must be included * Tell if all the federated servers must be included
*/ */
val includeAllNetworks: Boolean = false, val includeAllNetworks: Boolean = false
/**
* the avatar url
*/
val avatarUrl: String? = null
) { ) {
companion object { 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") Timber.w("Try to join an already joining room. Should not happen")
return@withState return@withState
} }
val viaServers = state.roomDirectoryData.homeServer val viaServers = listOfNotNull(state.roomDirectoryData.homeServer)
?.let { listOf(it) }
.orEmpty()
viewModelScope.launch { viewModelScope.launch {
try { try {
session.joinRoom(action.roomId, viaServers = viaServers) 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.R
import im.vector.app.core.resources.StringArrayProvider import im.vector.app.core.resources.StringArrayProvider
import im.vector.app.features.roomdirectory.RoomDirectoryData 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.Session
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
import javax.inject.Inject import javax.inject.Inject
class RoomDirectoryListCreator @Inject constructor(private val stringArrayProvider: StringArrayProvider, class RoomDirectoryListCreator @Inject constructor(
private val session: Session) { private val stringArrayProvider: StringArrayProvider,
private val session: Session
) {
fun computeDirectories(thirdPartyProtocolData: Map<String, ThirdPartyProtocol>): List<RoomDirectoryData> { fun computeDirectories(thirdPartyProtocolData: Map<String, ThirdPartyProtocol>): List<RoomDirectoryServer> {
val result = ArrayList<RoomDirectoryData>() val result = ArrayList<RoomDirectoryServer>()
val protocols = ArrayList<RoomDirectoryData>()
// Add user homeserver name // Add user homeserver name
val userHsName = session.myUserId.substringAfter(":") val userHsName = session.myUserId.substringAfter(":")
result.add(RoomDirectoryData( // Add default protocol
displayName = userHsName, protocols.add(
includeAllNetworks = true RoomDirectoryData(
)) homeServer = null,
displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME,
// Add user's HS but for Matrix public rooms only includeAllNetworks = false
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 result of the request // Add result of the request
thirdPartyProtocolData.forEach { thirdPartyProtocolData.forEach {
it.value.instances?.forEach { thirdPartyProtocolInstance -> it.value.instances?.forEach { thirdPartyProtocolInstance ->
result.add(RoomDirectoryData( protocols.add(
homeServer = null, RoomDirectoryData(
displayName = thirdPartyProtocolInstance.desc ?: "", homeServer = null,
thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId, displayName = thirdPartyProtocolInstance.desc ?: "",
includeAllNetworks = false, thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId,
// Default to protocol icon includeAllNetworks = false,
avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon // 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 return result
} }
} }

View File

@ -21,33 +21,38 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import im.vector.app.R 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.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.error.ErrorFormatter 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.core.resources.StringProvider
import im.vector.app.features.roomdirectory.RoomDirectoryData import im.vector.app.features.roomdirectory.RoomDirectoryData
import im.vector.app.features.roomdirectory.RoomDirectoryServer
import javax.inject.Inject import javax.inject.Inject
class RoomDirectoryPickerController @Inject constructor(private val stringProvider: StringProvider, class RoomDirectoryPickerController @Inject constructor(
private val errorFormatter: ErrorFormatter, private val stringProvider: StringProvider,
private val roomDirectoryListCreator: RoomDirectoryListCreator colorProvider: ColorProvider,
private val errorFormatter: ErrorFormatter,
private val roomDirectoryListCreator: RoomDirectoryListCreator
) : TypedEpoxyController<RoomDirectoryPickerViewState>() { ) : TypedEpoxyController<RoomDirectoryPickerViewState>() {
var callback: Callback? = null var callback: Callback? = null
var index = 0 private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
override fun buildModels(viewState: RoomDirectoryPickerViewState) { override fun buildModels(viewState: RoomDirectoryPickerViewState) {
val host = this val host = this
val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest
when (asyncThirdPartyProtocol) { when (val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest) {
is Success -> { is Success -> {
val directories = roomDirectoryListCreator.computeDirectories(asyncThirdPartyProtocol()) val directories = roomDirectoryListCreator.computeDirectories(asyncThirdPartyProtocol())
directories.join(
directories.forEach { each = { _, roomDirectoryServer -> buildDirectory(roomDirectoryServer) },
buildDirectory(it) between = { idx, _ -> buildDivider(idx) }
} )
} }
is Incomplete -> { is Incomplete -> {
loadingItem { 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 val host = this
roomDirectoryItem { dividerItem {
id(host.index++) 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 { if (roomDirectoryServer.isUserServer) {
roomDirectoryData.includeAllNetworks -> serverDescription(host.stringProvider.getString(R.string.directory_your_server))
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
} }
}
directoryDescription(description) roomDirectoryServer.protocols.forEach { roomDirectoryData ->
directoryAvatarUrl(roomDirectoryData.avatarUrl) roomDirectoryItem {
includeAllNetworks(roomDirectoryData.includeAllNetworks) 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 { globalListener {
host.callback?.onRoomDirectoryClicked(roomDirectoryData) 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"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
@ -15,23 +14,23 @@
android:id="@+id/itemRoomDirectoryAvatar" android:id="@+id/itemRoomDirectoryAvatar"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginStart="8dp" android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:background="@drawable/circle" android:background="@drawable/circle"
android:contentDescription="@string/avatar" android:contentDescription="@string/avatar"
android:padding="8dp" android:padding="8dp"
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" /> tools:src="@drawable/network_matrix" />
<TextView <TextView
android:id="@+id/itemRoomDirectoryName" android:id="@+id/itemRoomDirectoryName"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
android:maxLines="2" android:maxLines="2"
@ -49,26 +48,19 @@
android:id="@+id/itemRoomDirectoryDescription" android:id="@+id/itemRoomDirectoryDescription"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
android:maxLines="2" android:maxLines="2"
android:textColor="?riotx_text_primary" android:textColor="?riotx_text_primary"
android:textSize="15sp" android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/itemRoomDirectoryAvatar" app:layout_constraintStart_toEndOf="@id/itemRoomDirectoryAvatar"
app:layout_constraintTop_toBottomOf="@id/itemRoomDirectoryName" app:layout_constraintTop_toBottomOf="@id/itemRoomDirectoryName"
tools:text="@string/directory_server_all_rooms_on_server" /> tools:text="@string/directory_server_native_rooms"
tools:visibility="visible" />
<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" />
</androidx.constraintlayout.widget.ConstraintLayout> </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>