mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 10:25:35 +03:00
Join room from room preview
This commit is contained in:
parent
33fbcc8ba3
commit
6244913ab9
14 changed files with 277 additions and 32 deletions
|
@ -28,9 +28,10 @@ class ErrorFormatter(val stringProvider: StringProvider) {
|
|||
return failure.localizedMessage
|
||||
}
|
||||
|
||||
fun toHumanReadable(throwable: Throwable): String {
|
||||
fun toHumanReadable(throwable: Throwable?): String {
|
||||
|
||||
return when (throwable) {
|
||||
null -> ""
|
||||
is Failure.NetworkConnection -> stringProvider.getString(R.string.error_no_network)
|
||||
else -> throwable.localizedMessage
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
|
@ -43,14 +44,13 @@ class ButtonStateView @JvmOverloads constructor(context: Context, attrs: Attribu
|
|||
fun onRetryClicked()
|
||||
}
|
||||
|
||||
// Big or Flat button
|
||||
var button: Button
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.view_button_state, this)
|
||||
layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
buttonStateButton.setOnClickListener {
|
||||
callback?.onButtonClicked()
|
||||
}
|
||||
|
||||
buttonStateRetry.setOnClickListener {
|
||||
callback?.onRetryClicked()
|
||||
}
|
||||
|
@ -62,20 +62,32 @@ class ButtonStateView @JvmOverloads constructor(context: Context, attrs: Attribu
|
|||
0, 0)
|
||||
.apply {
|
||||
try {
|
||||
buttonStateButton.text = getString(R.styleable.ButtonStateView_bsv_button_text)
|
||||
if (getBoolean(R.styleable.ButtonStateView_bsv_use_flat_button, true)) {
|
||||
button = buttonStateButtonFlat
|
||||
buttonStateButtonBig.isVisible = false
|
||||
} else {
|
||||
button = buttonStateButtonBig
|
||||
buttonStateButtonFlat.isVisible = false
|
||||
}
|
||||
|
||||
button.text = getString(R.styleable.ButtonStateView_bsv_button_text)
|
||||
buttonStateLoaded.setImageDrawable(getDrawable(R.styleable.ButtonStateView_bsv_loaded_image_src))
|
||||
} finally {
|
||||
recycle()
|
||||
}
|
||||
}
|
||||
|
||||
button.setOnClickListener {
|
||||
callback?.onButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
fun render(newState: State) {
|
||||
if (newState == State.Button) {
|
||||
buttonStateButton.isVisible = true
|
||||
button.isVisible = true
|
||||
} else {
|
||||
// We use isInvisible because we want to keep button space in the layout
|
||||
buttonStateButton.isInvisible = true
|
||||
button.isInvisible = true
|
||||
}
|
||||
|
||||
buttonStateLoading.isVisible = newState == State.Loading
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2019 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.riotredesign.features.roomdirectory
|
||||
|
||||
/**
|
||||
* Join state of a room
|
||||
*/
|
||||
enum class JoinState {
|
||||
NOT_JOINED,
|
||||
JOINING,
|
||||
JOINING_ERROR,
|
||||
// Room is joined and this is confirmed by the sync
|
||||
JOINED
|
||||
}
|
|
@ -30,13 +30,6 @@ import im.vector.riotredesign.features.home.AvatarRenderer
|
|||
@EpoxyModelClass(layout = R.layout.item_public_room)
|
||||
abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
|
||||
|
||||
enum class JoinState {
|
||||
NOT_JOINED,
|
||||
JOINING,
|
||||
JOINING_ERROR,
|
||||
JOINED
|
||||
}
|
||||
|
||||
@EpoxyAttribute
|
||||
var avatarUrl: String? = null
|
||||
|
||||
|
|
|
@ -85,10 +85,10 @@ class PublicRoomsController(private val stringProvider: StringProvider,
|
|||
nbOfMembers(publicRoom.numJoinedMembers)
|
||||
|
||||
val joinState = when {
|
||||
viewState.joinedRoomsIds.contains(publicRoom.roomId) -> PublicRoomItem.JoinState.JOINED
|
||||
viewState.joiningRoomsIds.contains(publicRoom.roomId) -> PublicRoomItem.JoinState.JOINING
|
||||
viewState.joiningErrorRoomsIds.contains(publicRoom.roomId) -> PublicRoomItem.JoinState.JOINING_ERROR
|
||||
else -> PublicRoomItem.JoinState.NOT_JOINED
|
||||
viewState.joinedRoomsIds.contains(publicRoom.roomId) -> JoinState.JOINED
|
||||
viewState.joiningRoomsIds.contains(publicRoom.roomId) -> JoinState.JOINING
|
||||
viewState.joiningErrorRoomsIds.contains(publicRoom.roomId) -> JoinState.JOINING_ERROR
|
||||
else -> JoinState.NOT_JOINED
|
||||
}
|
||||
|
||||
joinState(joinState)
|
||||
|
@ -103,7 +103,7 @@ class PublicRoomsController(private val stringProvider: StringProvider,
|
|||
}
|
||||
|
||||
interface Callback {
|
||||
fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: PublicRoomItem.JoinState)
|
||||
fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState)
|
||||
fun onPublicRoomJoin(publicRoom: PublicRoom)
|
||||
fun loadMore()
|
||||
}
|
||||
|
|
|
@ -117,21 +117,21 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
|
|||
publicRoomsList.setController(publicRoomsController)
|
||||
}
|
||||
|
||||
override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: PublicRoomItem.JoinState) {
|
||||
override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) {
|
||||
Timber.v("PublicRoomClicked: $publicRoom")
|
||||
|
||||
when (joinState) {
|
||||
PublicRoomItem.JoinState.JOINED -> {
|
||||
JoinState.JOINED -> {
|
||||
val args = RoomDetailArgs(publicRoom.roomId)
|
||||
val roomDetailIntent = RoomDetailActivity.newIntent(requireActivity(), args)
|
||||
requireActivity().startActivity(roomDetailIntent)
|
||||
}
|
||||
PublicRoomItem.JoinState.NOT_JOINED,
|
||||
PublicRoomItem.JoinState.JOINING_ERROR -> {
|
||||
JoinState.NOT_JOINED,
|
||||
JoinState.JOINING_ERROR -> {
|
||||
// ROOM PREVIEW
|
||||
requireActivity().startActivity(RoomPreviewActivity.getIntent(requireActivity(), publicRoom))
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
Snackbar.make(publicRoomsCoordinator, getString(R.string.please_wait), Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
|
|
@ -19,13 +19,20 @@ package im.vector.riotredesign.features.roomdirectory.roompreview
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionManager
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.error.ErrorFormatter
|
||||
import im.vector.riotredesign.core.extensions.setTextOrHide
|
||||
import im.vector.riotredesign.core.platform.ButtonStateView
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
import im.vector.riotredesign.features.roomdirectory.JoinState
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
|
||||
import kotlinx.android.synthetic.main.fragment_room_preview_no_preview.*
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.android.scope.ext.android.bindScope
|
||||
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||
|
||||
|
@ -37,12 +44,17 @@ class RoomPreviewNoPreviewFragment : VectorBaseFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private val errorFormatter = get<ErrorFormatter>()
|
||||
private val roomPreviewViewModel: RoomPreviewViewModel by fragmentViewModel()
|
||||
|
||||
private val roomPreviewData: RoomPreviewData by args()
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
|
||||
setupToolbar(roomPreviewNoPreviewToolbar)
|
||||
|
||||
roomPreviewViewModel.init(roomPreviewData.roomId)
|
||||
}
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_room_preview_no_preview
|
||||
|
@ -54,8 +66,35 @@ class RoomPreviewNoPreviewFragment : VectorBaseFragment() {
|
|||
roomPreviewNoPreviewName.text = roomPreviewData.roomName
|
||||
roomPreviewNoPreviewTopic.setTextOrHide(roomPreviewData.topic)
|
||||
|
||||
roomPreviewNoPreviewJoin.setOnClickListener {
|
||||
vectorBaseActivity.notImplemented("Join from preview")
|
||||
roomPreviewNoPreviewJoin.callback = object : ButtonStateView.Callback {
|
||||
override fun onButtonClicked() {
|
||||
roomPreviewViewModel.joinRoom()
|
||||
}
|
||||
|
||||
override fun onRetryClicked() {
|
||||
// Same action
|
||||
onButtonClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(roomPreviewViewModel) { state ->
|
||||
TransitionManager.beginDelayedTransition(roomPreviewNoPreviewRoot)
|
||||
|
||||
roomPreviewNoPreviewJoin.render(
|
||||
when (state.roomJoinState) {
|
||||
JoinState.NOT_JOINED -> ButtonStateView.State.Button
|
||||
JoinState.JOINING -> ButtonStateView.State.Loading
|
||||
JoinState.JOINED -> ButtonStateView.State.Loaded
|
||||
JoinState.JOINING_ERROR -> ButtonStateView.State.Error
|
||||
}
|
||||
)
|
||||
|
||||
roomPreviewNoPreviewError.setTextOrHide(errorFormatter.toHumanReadable(state.lastError))
|
||||
|
||||
if (state.roomJoinState == JoinState.JOINED) {
|
||||
// TODO Quit this screen and open the room
|
||||
vectorBaseActivity.notImplemented("Open newly join room")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2019 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.riotredesign.features.roomdirectory.roompreview
|
||||
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import im.vector.riotredesign.features.roomdirectory.JoinState
|
||||
import org.koin.android.ext.android.get
|
||||
import timber.log.Timber
|
||||
|
||||
class RoomPreviewViewModel(initialState: RoomPreviewViewState,
|
||||
private val session: Session) : VectorViewModel<RoomPreviewViewState>(initialState) {
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomPreviewViewModel, RoomPreviewViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: RoomPreviewViewState): RoomPreviewViewModel? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
|
||||
return RoomPreviewViewModel(state, currentSession)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
// Observe joined room (from the sync)
|
||||
observeJoinedRooms()
|
||||
}
|
||||
|
||||
private fun observeJoinedRooms() {
|
||||
session
|
||||
.rx()
|
||||
.liveRoomSummaries()
|
||||
.execute { async ->
|
||||
val isRoomJoined = async.invoke()
|
||||
// Keep only joined room
|
||||
?.filter { it.membership == Membership.JOIN }
|
||||
?.map { it.roomId }
|
||||
?.toList()
|
||||
?.contains(roomId) == true
|
||||
|
||||
if (isRoomJoined) {
|
||||
copy(
|
||||
roomJoinState = JoinState.JOINED
|
||||
)
|
||||
} else {
|
||||
// TODO No change...
|
||||
copy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO I should not have to do that
|
||||
fun init(roomId: String) = withState {
|
||||
setState {
|
||||
copy(
|
||||
roomId = roomId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun joinRoom() = withState { state ->
|
||||
if (state.roomJoinState == JoinState.JOINING) {
|
||||
// Request already sent, should not happen
|
||||
Timber.w("Try to join an already joining room. Should not happen")
|
||||
return@withState
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
roomJoinState = JoinState.JOINING,
|
||||
lastError = null
|
||||
)
|
||||
}
|
||||
|
||||
session.joinRoom(state.roomId, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||
// Instead, we wait for the room to be joined
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
roomJoinState = JoinState.JOINING_ERROR,
|
||||
lastError = failure
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2019 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.riotredesign.features.roomdirectory.roompreview
|
||||
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import im.vector.riotredesign.features.roomdirectory.JoinState
|
||||
|
||||
data class RoomPreviewViewState(
|
||||
// The room id
|
||||
val roomId: String = "",
|
||||
// Current state of the room in preview
|
||||
val roomJoinState: JoinState = JoinState.NOT_JOINED,
|
||||
// Last error of join room request
|
||||
val lastError: Throwable? = null
|
||||
) : MvRxState
|
|
@ -1,5 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/roomPreviewNoPreviewRoot"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
@ -64,15 +66,31 @@
|
|||
android:textAppearance="@style/TextAppearance.Vector.Subtitle2"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/roomPreviewNoPreviewJoin"
|
||||
style="@style/VectorButtonStyle"
|
||||
<TextView
|
||||
android:id="@+id/roomPreviewNoPreviewError"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/vector_error_color"
|
||||
android:textSize="15sp"
|
||||
android:visibility="gone"
|
||||
tools:text="Error"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<im.vector.riotredesign.core.platform.ButtonStateView
|
||||
android:id="@+id/roomPreviewNoPreviewJoin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="@dimen/layout_vertical_margin"
|
||||
android:minWidth="120dp"
|
||||
android:text="@string/join" />
|
||||
app:bsv_button_text="@string/join"
|
||||
app:bsv_loaded_image_src="@drawable/ic_tick"
|
||||
app:bsv_use_flat_button="false"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
app:bsv_use_flat_button="true"
|
||||
app:bsv_button_text="@string/join"
|
||||
app:bsv_loaded_image_src="@drawable/ic_tick"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
tools:parentTag="android.widget.FrameLayout">
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonStateButton"
|
||||
android:id="@+id/buttonStateButtonFlat"
|
||||
style="@style/VectorButtonStyleFlat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -17,6 +17,18 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/join" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonStateButtonBig"
|
||||
style="@style/VectorButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:minWidth="120dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/join" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/buttonStateLoading"
|
||||
android:layout_width="32dp"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
<declare-styleable name="ButtonStateView">
|
||||
|
||||
<attr name="bsv_use_flat_button" format="boolean" />
|
||||
<attr name="bsv_loaded_image_src" format="reference" />
|
||||
<attr name="bsv_button_text" format="reference|string" />
|
||||
|
||||
|
|
Loading…
Reference in a new issue