Add InviteUsersToRoomActivity and mvrx classes.

This commit is contained in:
onurays 2020-04-30 13:54:09 +03:00
parent a4eba653a3
commit 57a87ba620
17 changed files with 347 additions and 1 deletions

View file

@ -28,6 +28,7 @@ import im.vector.matrix.android.api.session.room.send.UserDraft
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.api.util.toOptional
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single
@ -95,6 +96,10 @@ class RxRoom(private val room: Room) {
fun liveNotificationState(): Observable<RoomNotificationState> {
return room.getLiveRoomNotificationState().asObservable()
}
fun invite(userId: String, reason: String? = null): Completable = completableBuilder<Unit> {
room.invite(userId, reason, it)
}
}
fun Room.rx(): RxRoom {

View file

@ -39,6 +39,8 @@ internal class DefaultInviteTask @Inject constructor(
return executeRequest(eventBus) {
val body = InviteBody(params.userId, params.reason)
apiCall = roomAPI.invite(params.roomId, body)
isRetryable = true
maxRetryCount = 3
}
}
}

View file

@ -85,6 +85,7 @@
</activity>
<activity android:name=".features.debug.DebugMenuActivity" />
<activity android:name="im.vector.riotx.features.createdirect.CreateDirectRoomActivity" />
<activity android:name="im.vector.riotx.features.invite.InviteUsersToRoomActivity" />
<activity android:name=".features.webview.VectorWebViewActivity" />
<activity android:name=".features.link.LinkHandlerActivity">
<intent-filter>

View file

@ -39,6 +39,7 @@ import im.vector.riotx.features.home.room.detail.timeline.reactions.ViewReaction
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
import im.vector.riotx.features.home.room.list.RoomListModule
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.riotx.features.invite.InviteUsersToRoomActivity
import im.vector.riotx.features.invite.VectorInviteView
import im.vector.riotx.features.link.LinkHandlerActivity
import im.vector.riotx.features.login.LoginActivity
@ -116,6 +117,7 @@ interface ScreenComponent {
fun inject(activity: DebugMenuActivity)
fun inject(activity: SharedSecureStorageActivity)
fun inject(activity: BigImageViewerActivity)
fun inject(activity: InviteUsersToRoomActivity)
/* ==========================================================================================
* BottomSheets

View file

@ -26,6 +26,7 @@ import im.vector.matrix.android.api.session.Session
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.hideKeyboard
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.activity.*
import javax.inject.Inject
@ -107,4 +108,15 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() {
}
super.onBackPressed()
}
protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
viewEvents
.observe()
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
hideWaitingView()
observer(it)
}
.disposeOnDestroy()
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 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.riotx.features.invite
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class InviteUsersToRoomAction : VectorViewModelAction {
data class InviteSelectedUsers(val selectedUsers: Set<User>) : InviteUsersToRoomAction()
}

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2020 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.riotx.features.invite
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.error.ErrorFormatter
import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.extensions.addFragmentToBackstack
import im.vector.riotx.core.platform.SimpleFragmentActivity
import im.vector.riotx.core.platform.WaitingViewData
import im.vector.riotx.core.utils.toast
import im.vector.riotx.features.userdirectory.KnownUsersFragment
import im.vector.riotx.features.userdirectory.KnownUsersFragmentArgs
import im.vector.riotx.features.userdirectory.UserDirectoryFragment
import im.vector.riotx.features.userdirectory.UserDirectorySharedAction
import im.vector.riotx.features.userdirectory.UserDirectorySharedActionViewModel
import im.vector.riotx.features.userdirectory.UserDirectoryViewModel
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity.*
import java.net.HttpURLConnection
import javax.inject.Inject
@Parcelize
data class InviteUsersToRoomArgs(val roomId: String) : Parcelable
class InviteUsersToRoomActivity : SimpleFragmentActivity() {
private val viewModel: InviteUsersToRoomViewModel by viewModel()
private lateinit var sharedActionViewModel: UserDirectorySharedActionViewModel
@Inject lateinit var userDirectoryViewModelFactory: UserDirectoryViewModel.Factory
@Inject lateinit var inviteUsersToRoomViewModelFactory: InviteUsersToRoomViewModel.Factory
@Inject lateinit var errorFormatter: ErrorFormatter
override fun injectWith(injector: ScreenComponent) {
super.injectWith(injector)
injector.inject(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
toolbar.visibility = View.GONE
sharedActionViewModel = viewModelProvider.get(UserDirectorySharedActionViewModel::class.java)
sharedActionViewModel
.observe()
.subscribe { sharedAction ->
when (sharedAction) {
UserDirectorySharedAction.OpenUsersDirectory ->
addFragmentToBackstack(R.id.container, UserDirectoryFragment::class.java)
UserDirectorySharedAction.Close -> finish()
UserDirectorySharedAction.GoBack -> onBackPressed()
is UserDirectorySharedAction.OnMenuItemSelected -> onMenuItemSelected(sharedAction)
}
}
.disposeOnDestroy()
if (isFirstCreation()) {
addFragment(
R.id.container,
KnownUsersFragment::class.java,
KnownUsersFragmentArgs(
title = getString(R.string.invite_users_to_room_title),
menuResId = R.menu.vector_invite_users_to_room
)
)
}
viewModel.observeViewEvents { renderInviteEvents(it) }
}
private fun onMenuItemSelected(action: UserDirectorySharedAction.OnMenuItemSelected) {
if (action.itemId == R.id.action_invite_users_to_room_invite) {
viewModel.handle(InviteUsersToRoomAction.InviteSelectedUsers(action.selectedUsers))
}
}
private fun renderInviteEvents(viewEvent: InviteUsersToRoomViewEvents) {
when (viewEvent) {
is InviteUsersToRoomViewEvents.Loading -> renderInviteLoading()
is InviteUsersToRoomViewEvents.Success -> renderInvitationSuccess()
is InviteUsersToRoomViewEvents.Failure -> renderInviteFailure(viewEvent.throwable)
}
}
private fun renderInviteLoading() {
updateWaitingView(WaitingViewData(getString(R.string.inviting_users_to_room)))
}
private fun renderInviteFailure(error: Throwable) {
hideWaitingView()
if (error is CreateRoomFailure.CreatedWithTimeout) {
finish()
} else {
val message = if (error is Failure.ServerError && error.httpCode == HttpURLConnection.HTTP_INTERNAL_ERROR /*500*/) {
// This error happen if the invited userId does not exist.
getString(R.string.invite_users_to_room_failure)
} else {
errorFormatter.toHumanReadable(error)
}
AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton(R.string.ok, null)
.show()
}
}
private fun renderInvitationSuccess() = withState(viewModel) {
toast(R.string.invitations_sent_successfully)
finish()
}
companion object {
fun getIntent(context: Context, roomId: String): Intent {
return Intent(context, InviteUsersToRoomActivity::class.java).also {
it.putExtra(MvRx.KEY_ARG, InviteUsersToRoomArgs(roomId))
}
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2020 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.riotx.features.invite
import im.vector.riotx.core.platform.VectorViewEvents
sealed class InviteUsersToRoomViewEvents : VectorViewEvents {
object Loading : InviteUsersToRoomViewEvents()
data class Failure(val throwable: Throwable) : InviteUsersToRoomViewEvents()
object Success : InviteUsersToRoomViewEvents()
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2020 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.riotx.features.invite
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable
class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted
initialState: InviteUsersToRoomViewState,
session: Session)
: VectorViewModel<InviteUsersToRoomViewState, InviteUsersToRoomAction, InviteUsersToRoomViewEvents>(initialState) {
private val room = session.getRoom(initialState.roomId)!!
@AssistedInject.Factory
interface Factory {
fun create(initialState: InviteUsersToRoomViewState): InviteUsersToRoomViewModel
}
companion object : MvRxViewModelFactory<InviteUsersToRoomViewModel, InviteUsersToRoomViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: InviteUsersToRoomViewState): InviteUsersToRoomViewModel? {
val activity: InviteUsersToRoomActivity = (viewModelContext as ActivityViewModelContext).activity()
return activity.inviteUsersToRoomViewModelFactory.create(state)
}
}
override fun handle(action: InviteUsersToRoomAction) {
when (action) {
is InviteUsersToRoomAction.InviteSelectedUsers -> inviteUsersToRoom(action.selectedUsers)
}
}
private fun inviteUsersToRoom(selectedUsers: Set<User>) {
_viewEvents.post(InviteUsersToRoomViewEvents.Loading)
Observable.fromIterable(selectedUsers).flatMapCompletable { user ->
room.rx().invite(user.userId, null)
}.subscribe(
{
_viewEvents.post(InviteUsersToRoomViewEvents.Success)
},
{
_viewEvents.post(InviteUsersToRoomViewEvents.Failure(it))
}).disposeOnClear()
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 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.riotx.features.invite
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
data class InviteUsersToRoomViewState(
val roomId: String,
val inviteState: Async<Unit> = Uninitialized
) : MvRxState {
constructor(args: InviteUsersToRoomArgs) : this(roomId = args.roomId)
}

View file

@ -41,6 +41,7 @@ import im.vector.riotx.features.debug.DebugMenuActivity
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
import im.vector.riotx.features.invite.InviteUsersToRoomActivity
import im.vector.riotx.features.media.BigImageViewerActivity
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
@ -163,6 +164,11 @@ class DefaultNavigator @Inject constructor(
context.startActivity(intent)
}
override fun openInviteUsersToRoom(context: Context, roomId: String) {
val intent = InviteUsersToRoomActivity.getIntent(context, roomId)
context.startActivity(intent)
}
override fun openRoomsFiltering(context: Context) {
val intent = FilteredRoomsActivity.newIntent(context)
context.startActivity(intent)

View file

@ -46,6 +46,8 @@ interface Navigator {
fun openCreateDirectRoom(context: Context)
fun openInviteUsersToRoom(context: Context, roomId: String)
fun openRoomDirectory(context: Context, initialFilter: String = "")
fun openRoomsFiltering(context: Context)

View file

@ -49,7 +49,7 @@ class RoomMemberListFragment @Inject constructor(
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_room_member_list_add_member -> {
navigator.openCreateDirectRoom(requireContext())
navigator.openInviteUsersToRoom(requireContext(), roomProfileArgs.roomId)
return true
}
}

View file

@ -23,6 +23,7 @@ import android.view.View
import android.widget.ScrollView
import androidx.core.view.forEach
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.args
import com.airbnb.mvrx.withState
import com.google.android.material.chip.Chip
import com.jakewharton.rxbinding3.widget.textChanges
@ -43,6 +44,8 @@ class KnownUsersFragment @Inject constructor(
private val dimensionConverter: DimensionConverter
) : VectorBaseFragment(), KnownUsersController.Callback {
private val args: KnownUsersFragmentArgs by args()
override fun getLayoutResId() = R.layout.fragment_known_users
override fun getMenuRes() = withState(viewModel) {
@ -55,6 +58,9 @@ class KnownUsersFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(UserDirectorySharedActionViewModel::class.java)
knownUsersTitle.text = args.title
vectorBaseActivity.setSupportActionBar(knownUsersToolbar)
setupRecyclerView()
setupFilterView()

View file

@ -31,6 +31,7 @@ import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.toggle
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.createdirect.CreateDirectRoomActivity
import im.vector.riotx.features.invite.InviteUsersToRoomActivity
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit
@ -59,6 +60,7 @@ class UserDirectoryViewModel @AssistedInject constructor(@Assisted
is ActivityViewModelContext -> {
when (viewModelContext.activity<FragmentActivity>()) {
is CreateDirectRoomActivity -> viewModelContext.activity<CreateDirectRoomActivity>().userDirectoryViewModelFactory.create(state)
is InviteUsersToRoomActivity -> viewModelContext.activity<InviteUsersToRoomActivity>().userDirectoryViewModelFactory.create(state)
else -> error("Wrong activity or fragment")
}
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_invite_users_to_room_invite"
android:title="@string/invite_users_to_room_action_invite"
app:showAsAction="always" />
</menu>

View file

@ -37,6 +37,11 @@
<string name="external_link_confirmation_title">Double-check this link</string>
<string name="external_link_confirmation_message">The link %1$s is taking you to another site: %2$s.\n\nAre you sure you want to continue?</string>
<string name="add_members_to_room">Add members</string>
<string name="invite_users_to_room_action_invite">INVITE</string>
<string name="inviting_users_to_room">Inviting users…</string>
<string name="invite_users_to_room_title">Invite Users</string>
<string name="invitations_sent_successfully">Invitation sent successfully</string>
<string name="invite_users_to_room_failure">We could not invite users. Please check the users you want to invite and try again.</string>
<!-- END Strings added by Onuray -->