Add error feedback when joining space rooms

This commit is contained in:
Valere 2021-06-16 17:35:00 +02:00
parent 71b456c57e
commit e277deece5
7 changed files with 51 additions and 10 deletions

1
newsfragment/3207.bugfix Normal file
View File

@ -0,0 +1 @@
Space Explore Rooms no feedback on failed to join

View File

@ -17,11 +17,13 @@
package im.vector.app.features.home.room.list package im.vector.app.features.home.room.list
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter
@ -37,7 +39,8 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
private val dateFormatter: VectorDateFormatter, private val dateFormatter: VectorDateFormatter,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val typingHelper: TypingHelper, private val typingHelper: TypingHelper,
private val avatarRenderer: AvatarRenderer) { private val avatarRenderer: AvatarRenderer,
private val errorFormatter: ErrorFormatter) {
fun create(roomSummary: RoomSummary, fun create(roomSummary: RoomSummary,
roomChangeMembershipStates: Map<String, ChangeMembershipState>, roomChangeMembershipStates: Map<String, ChangeMembershipState>,
@ -55,12 +58,21 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
fun createSuggestion(spaceChildInfo: SpaceChildInfo, fun createSuggestion(spaceChildInfo: SpaceChildInfo,
suggestedRoomJoiningStates: Map<String, Async<Unit>>, suggestedRoomJoiningStates: Map<String, Async<Unit>>,
listener: RoomListListener?): VectorEpoxyModel<*> { listener: RoomListListener?): VectorEpoxyModel<*> {
val error = (suggestedRoomJoiningStates[spaceChildInfo.childRoomId] as? Fail)?.error
return SpaceChildInfoItem_() return SpaceChildInfoItem_()
.id("sug_${spaceChildInfo.childRoomId}") .id("sug_${spaceChildInfo.childRoomId}")
.matrixItem(spaceChildInfo.toMatrixItem()) .matrixItem(spaceChildInfo.toMatrixItem())
.avatarRenderer(avatarRenderer) .avatarRenderer(avatarRenderer)
.topic(spaceChildInfo.topic) .topic(spaceChildInfo.topic)
.buttonLabel(stringProvider.getString(R.string.join)) .errorLabel(
error?.let {
stringProvider.getString(R.string.error_failed_to_join_room, errorFormatter.toHumanReadable(it))
}
)
.buttonLabel(
if (error != null) stringProvider.getString(R.string.global_retry)
else stringProvider.getString(R.string.join)
)
.loading(suggestedRoomJoiningStates[spaceChildInfo.childRoomId] is Loading) .loading(suggestedRoomJoiningStates[spaceChildInfo.childRoomId] is Loading)
.memberCount(spaceChildInfo.activeMemberCount ?: 0) .memberCount(spaceChildInfo.activeMemberCount ?: 0)
.buttonClickListener { listener?.onJoinSuggestedRoom(spaceChildInfo) } .buttonClickListener { listener?.onJoinSuggestedRoom(spaceChildInfo) }

View File

@ -33,6 +33,7 @@ import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import me.gujun.android.span.image import me.gujun.android.span.image
@ -52,6 +53,7 @@ abstract class SpaceChildInfoItem : VectorEpoxyModel<SpaceChildInfoItem.Holder>(
@EpoxyAttribute var loading: Boolean = false @EpoxyAttribute var loading: Boolean = false
@EpoxyAttribute var buttonLabel: String? = null @EpoxyAttribute var buttonLabel: String? = null
@EpoxyAttribute var errorLabel: String? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemLongClickListener: View.OnLongClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemLongClickListener: View.OnLongClickListener? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: ClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: ClickListener? = null
@ -97,6 +99,8 @@ abstract class SpaceChildInfoItem : VectorEpoxyModel<SpaceChildInfoItem.Holder>(
holder.joinButton.isVisible = true holder.joinButton.isVisible = true
} }
holder.errorTextView.setTextOrHide(errorLabel)
holder.joinButton.onClick { holder.joinButton.onClick {
// local echo // local echo
holder.joinButton.isEnabled = false holder.joinButton.isEnabled = false
@ -120,5 +124,6 @@ abstract class SpaceChildInfoItem : VectorEpoxyModel<SpaceChildInfoItem.Holder>(
val descriptionText by bind<TextView>(R.id.suggestedRoomDescription) val descriptionText by bind<TextView>(R.id.suggestedRoomDescription)
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView) val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
val rootView by bind<ViewGroup>(R.id.itemRoomLayout) val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
val errorTextView by bind<TextView>(R.id.inlineErrorText)
} }
} }

View File

@ -35,6 +35,7 @@ import im.vector.app.features.home.room.list.spaceChildInfoItem
import me.gujun.android.span.span import me.gujun.android.span.span
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError.Companion.M_UNRECOGNIZED import org.matrix.android.sdk.api.failure.MatrixError.Companion.M_UNRECOGNIZED
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
@ -127,6 +128,7 @@ class SpaceDirectoryController @Inject constructor(
val isSpace = info.roomType == RoomType.SPACE val isSpace = info.roomType == RoomType.SPACE
val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true
val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false
val error = (data?.changeMembershipStates?.get(info.childRoomId) as? ChangeMembershipState.FailedJoining)?.throwable
// if it's known use that matrixItem because it would have a better computed name // if it's known use that matrixItem because it would have a better computed name
val matrixItem = data?.knownRoomSummaries?.find { it.roomId == info.childRoomId }?.toMatrixItem() val matrixItem = data?.knownRoomSummaries?.find { it.roomId == info.childRoomId }?.toMatrixItem()
?: info.toMatrixItem() ?: info.toMatrixItem()
@ -135,11 +137,19 @@ class SpaceDirectoryController @Inject constructor(
matrixItem(matrixItem) matrixItem(matrixItem)
avatarRenderer(host.avatarRenderer) avatarRenderer(host.avatarRenderer)
topic(info.topic) topic(info.topic)
errorLabel(
error?.let {
host.stringProvider.getString(R.string.error_failed_to_join_room, host.errorFormatter.toHumanReadable(it))
}
)
memberCount(info.activeMemberCount ?: 0) memberCount(info.activeMemberCount ?: 0)
loading(isLoading) loading(isLoading)
buttonLabel( buttonLabel(
if (isJoined) host.stringProvider.getString(R.string.action_open) when {
else host.stringProvider.getString(R.string.join) error != null -> host.stringProvider.getString(R.string.global_retry)
isJoined -> host.stringProvider.getString(R.string.action_open)
else -> host.stringProvider.getString(R.string.join)
}
) )
apply { apply {
if (isSpace) { if (isSpace) {

View File

@ -188,20 +188,21 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
private fun handleJoinOrOpen(spaceChildInfo: SpaceChildInfo) = withState { state -> private fun handleJoinOrOpen(spaceChildInfo: SpaceChildInfo) = withState { state ->
val isSpace = spaceChildInfo.roomType == RoomType.SPACE val isSpace = spaceChildInfo.roomType == RoomType.SPACE
if (state.joinedRoomsIds.contains(spaceChildInfo.childRoomId)) { val childId = spaceChildInfo.childRoomId
if (state.joinedRoomsIds.contains(childId)) {
if (isSpace) { if (isSpace) {
handle(SpaceDirectoryViewAction.ExploreSubSpace(spaceChildInfo)) handle(SpaceDirectoryViewAction.ExploreSubSpace(spaceChildInfo))
} else { } else {
_viewEvents.post(SpaceDirectoryViewEvents.NavigateToRoom(spaceChildInfo.childRoomId)) _viewEvents.post(SpaceDirectoryViewEvents.NavigateToRoom(childId))
} }
} else { } else {
// join // join
viewModelScope.launch { viewModelScope.launch {
try { try {
if (isSpace) { if (isSpace) {
session.spaceService().joinSpace(spaceChildInfo.childRoomId, null, spaceChildInfo.viaServers) session.spaceService().joinSpace(childId, null, spaceChildInfo.viaServers)
} else { } else {
session.joinRoom(spaceChildInfo.childRoomId, null, spaceChildInfo.viaServers) session.joinRoom(childId, null, spaceChildInfo.viaServers)
} }
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.e(failure, "## Space: Failed to join room or subspace") Timber.e(failure, "## Space: Failed to join room or subspace")

View File

@ -80,7 +80,7 @@
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:maxWidth="@dimen/button_max_width" android:maxWidth="@dimen/button_max_width"
android:text="@string/join" android:text="@string/join"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toTopOf="@id/inlineErrorText"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -102,6 +102,17 @@
app:barrierDirection="bottom" app:barrierDirection="bottom"
app:constraint_referenced_ids="roomAvatarBottomSpace" /> app:constraint_referenced_ids="roomAvatarBottomSpace" />
<TextView
android:id="@+id/inlineErrorText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="8dp"
android:padding="8dp"
android:textColor="@color/design_default_color_error"
app:drawableStartCompat="@drawable/ic_warning_badge"
app:layout_constraintTop_toBottomOf="@+id/roomBottomBarrier"
tools:text="An error occured while joining" />
<!-- We use vctr_list_separator_system here for a better rendering --> <!-- We use vctr_list_separator_system here for a better rendering -->
<View <View
android:id="@+id/roomDividerView" android:id="@+id/roomDividerView"
@ -111,6 +122,6 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/roomBottomBarrier" /> app:layout_constraintTop_toBottomOf="@+id/inlineErrorText" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -3403,4 +3403,5 @@
<string name="teammate_spaces_arent_quite_ready">"Teammate spaces arent quite ready but you can still give them a try"</string> <string name="teammate_spaces_arent_quite_ready">"Teammate spaces arent quite ready but you can still give them a try"</string>
<string name="teammate_spaces_might_not_join">"At the moment people might not be able to join any private rooms you make.\n\nWell be improving this as part of the beta, but just wanted to let you know."</string> <string name="teammate_spaces_might_not_join">"At the moment people might not be able to join any private rooms you make.\n\nWell be improving this as part of the beta, but just wanted to let you know."</string>
<string name="error_failed_to_join_room">Sorry, an error occurred while trying to join: %s</string>
</resources> </resources>