forked from GitHub-Mirror/riotX-android
Create direct room: almost finished, still need to handle showing selected users in search field
This commit is contained in:
parent
cb274d6a33
commit
cb44ab547c
@ -23,7 +23,6 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||
import im.vector.matrix.android.api.session.sync.SyncState
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
@ -53,6 +52,12 @@ class RxSession(private val session: Session) {
|
||||
session.createRoom(roomParams, MatrixCallbackSingle(it)).toSingle(it)
|
||||
}
|
||||
|
||||
fun searchUsersDirectory(search: String,
|
||||
limit: Int,
|
||||
excludedUserIds: Set<String>): Single<List<User>> = Single.create {
|
||||
session.searchUsersDirectory(search, limit, excludedUserIds, MatrixCallbackSingle(it)).toSingle(it)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Session.rx(): RxSession {
|
||||
|
@ -17,7 +17,9 @@
|
||||
package im.vector.matrix.android.api.session.user
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
|
||||
/**
|
||||
* This interface defines methods to get users. It's implemented at the session level.
|
||||
@ -31,6 +33,16 @@ interface UserService {
|
||||
*/
|
||||
fun getUser(userId: String): User?
|
||||
|
||||
/**
|
||||
* Search list of users on server directory.
|
||||
* @param search the searched term
|
||||
* @param limit the max number of users to return
|
||||
* @param excludedUserIds the user ids to filter from the search
|
||||
* @param callback the async callback
|
||||
* @return Cancelable
|
||||
*/
|
||||
fun searchUsersDirectory(search: String, limit: Int, excludedUserIds: Set<String>, callback: MatrixCallback<List<User>>): Cancelable
|
||||
|
||||
/**
|
||||
* Observe a live user from a userId
|
||||
* @param userId the userId to look for.
|
||||
|
@ -19,20 +19,25 @@ package im.vector.matrix.android.internal.session.user
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.user.UserService
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.database.RealmLiveData
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||
import im.vector.matrix.android.internal.database.model.UserEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.session.user.model.SearchUserTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.task.toConfigurableTask
|
||||
import im.vector.matrix.android.internal.util.fetchCopied
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultUserService @Inject constructor(private val monarchy: Monarchy) : UserService {
|
||||
internal class DefaultUserService @Inject constructor(private val monarchy: Monarchy,
|
||||
private val searchUserTask: SearchUserTask,
|
||||
private val taskExecutor: TaskExecutor) : UserService {
|
||||
|
||||
override fun getUser(userId: String): User? {
|
||||
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
|
||||
@ -62,4 +67,15 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
|
||||
{ it.asDomain() }
|
||||
)
|
||||
}
|
||||
|
||||
override fun searchUsersDirectory(search: String,
|
||||
limit: Int,
|
||||
excludedUserIds: Set<String>,
|
||||
callback: MatrixCallback<List<User>>): Cancelable {
|
||||
val params = SearchUserTask.Params(limit, search, excludedUserIds)
|
||||
return searchUserTask
|
||||
.configureWith(params)
|
||||
.dispatchTo(callback)
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.session.user
|
||||
|
||||
import im.vector.matrix.android.internal.network.NetworkConstants.URI_API_PREFIX_PATH_R0
|
||||
import im.vector.matrix.android.internal.session.user.model.SearchUsersParams
|
||||
import im.vector.matrix.android.internal.session.user.model.SearchUsersRequestResponse
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.POST
|
||||
|
||||
internal interface SearchUserAPI {
|
||||
|
||||
/**
|
||||
* Perform a user search.
|
||||
*
|
||||
* @param searchUsersParams the search params.
|
||||
*/
|
||||
@POST(URI_API_PREFIX_PATH_R0 + "user_directory/search")
|
||||
fun searchUsers(@Body searchUsersParams: SearchUsersParams): Call<SearchUsersRequestResponse>
|
||||
}
|
@ -18,12 +18,31 @@ package im.vector.matrix.android.internal.session.user
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.session.user.UserService
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.session.sync.SyncAPI
|
||||
import im.vector.matrix.android.internal.session.user.model.DefaultSearchUserTask
|
||||
import im.vector.matrix.android.internal.session.user.model.SearchUserTask
|
||||
import retrofit2.Retrofit
|
||||
|
||||
@Module
|
||||
internal abstract class UserModule {
|
||||
|
||||
@Module
|
||||
companion object {
|
||||
@Provides
|
||||
@JvmStatic
|
||||
@SessionScope
|
||||
fun providesSearchUserAPI(retrofit: Retrofit): SearchUserAPI {
|
||||
return retrofit.create(SearchUserAPI::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract fun bindUserService(userService: DefaultUserService): UserService
|
||||
|
||||
@Binds
|
||||
abstract fun bindSearchUserTask(searchUserTask: DefaultSearchUserTask): SearchUserTask
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.session.user.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SearchUser(
|
||||
@Json(name = "user_id") val userId: String,
|
||||
@Json(name = "display_name") val displayName: String? = null,
|
||||
@Json(name = "avatar_url") val avatarUrl: String? = null
|
||||
)
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.session.user.model
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.user.SearchUserAPI
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface SearchUserTask : Task<SearchUserTask.Params, List<User>> {
|
||||
|
||||
data class Params(
|
||||
val limit: Int,
|
||||
val search: String,
|
||||
val excludedUserIds: Set<String>
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultSearchUserTask @Inject constructor(private val searchUserAPI: SearchUserAPI) : SearchUserTask {
|
||||
|
||||
override suspend fun execute(params: SearchUserTask.Params): Try<List<User>> {
|
||||
return executeRequest<SearchUsersRequestResponse> {
|
||||
apiCall = searchUserAPI.searchUsers(SearchUsersParams(params.search, params.limit))
|
||||
}.map { response ->
|
||||
response.users.map {
|
||||
User(it.userId, it.displayName, it.avatarUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.session.user.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Class representing an user search parameters
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SearchUsersParams(
|
||||
// the searched term
|
||||
@Json(name = "search_term") val searchTerm: String,
|
||||
// set a limit to the request response
|
||||
@Json(name = "limit") val limit: Int
|
||||
)
|
@ -0,0 +1,14 @@
|
||||
package im.vector.matrix.android.internal.session.user.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Class representing an users search response
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SearchUsersRequestResponse(
|
||||
@Json(name = "limited") val limited: Boolean = false,
|
||||
@Json(name = "results") val users: List<SearchUser> = emptyList()
|
||||
)
|
||||
|
@ -36,6 +36,8 @@ import im.vector.riotx.features.home.HomeActivity
|
||||
import im.vector.riotx.features.home.HomeDetailFragment
|
||||
import im.vector.riotx.features.home.HomeDrawerFragment
|
||||
import im.vector.riotx.features.home.HomeModule
|
||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity
|
||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomDirectoryUsersFragment
|
||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomFragment
|
||||
import im.vector.riotx.features.home.group.GroupListFragment
|
||||
import im.vector.riotx.features.home.room.detail.RoomDetailFragment
|
||||
@ -46,6 +48,7 @@ import im.vector.riotx.features.invite.VectorInviteView
|
||||
import im.vector.riotx.features.login.LoginActivity
|
||||
import im.vector.riotx.features.media.ImageMediaViewerActivity
|
||||
import im.vector.riotx.features.media.VideoMediaViewerActivity
|
||||
import im.vector.riotx.features.navigation.Navigator
|
||||
import im.vector.riotx.features.rageshake.BugReportActivity
|
||||
import im.vector.riotx.features.rageshake.BugReporter
|
||||
import im.vector.riotx.features.rageshake.RageShake
|
||||
@ -74,6 +77,8 @@ interface ScreenComponent {
|
||||
|
||||
fun rageShake(): RageShake
|
||||
|
||||
fun navigator(): Navigator
|
||||
|
||||
fun inject(activity: HomeActivity)
|
||||
|
||||
fun inject(roomDetailFragment: RoomDetailFragment)
|
||||
@ -154,7 +159,11 @@ interface ScreenComponent {
|
||||
|
||||
fun inject(pushGatewaysFragment: PushGatewaysFragment)
|
||||
|
||||
fun inject(createDirectRoomFragment: CreateDirectRoomFragment)
|
||||
fun inject(createDirectRoomKnownUsersFragment: CreateDirectRoomFragment)
|
||||
|
||||
fun inject(createDirectRoomDirectoryUsersFragment: CreateDirectRoomDirectoryUsersFragment)
|
||||
|
||||
fun inject(createDirectRoomActivity: CreateDirectRoomActivity)
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
|
@ -30,6 +30,7 @@ import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsVie
|
||||
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
|
||||
import im.vector.riotx.features.crypto.verification.SasVerificationViewModel
|
||||
import im.vector.riotx.features.home.*
|
||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomNavigationViewModel
|
||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomViewModel
|
||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomViewModel_AssistedFactory
|
||||
import im.vector.riotx.features.home.group.GroupListViewModel
|
||||
@ -118,6 +119,11 @@ interface ViewModelModule {
|
||||
@ViewModelKey(ConfigurationViewModel::class)
|
||||
fun bindConfigurationViewModel(viewModel: ConfigurationViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(CreateDirectRoomNavigationViewModel::class)
|
||||
fun bindCreateDirectRoomNavigationViewModel(viewModel: CreateDirectRoomNavigationViewModel): ViewModel
|
||||
|
||||
/**
|
||||
* Below are bindings for the MvRx view models (which extend VectorViewModel). Will be the only usage in the future.
|
||||
*/
|
||||
|
@ -18,6 +18,7 @@ package im.vector.riotx.core.platform
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import butterknife.BindView
|
||||
@ -46,6 +47,7 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() {
|
||||
|
||||
@Inject lateinit var session: Session
|
||||
|
||||
@CallSuper
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
session = injector.session()
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import androidx.annotation.*
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
@ -40,6 +41,7 @@ import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.*
|
||||
import im.vector.riotx.core.utils.toast
|
||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
||||
import im.vector.riotx.features.navigation.Navigator
|
||||
import im.vector.riotx.features.rageshake.BugReportActivity
|
||||
import im.vector.riotx.features.rageshake.BugReporter
|
||||
import im.vector.riotx.features.rageshake.RageShake
|
||||
@ -70,6 +72,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
|
||||
private lateinit var configurationViewModel: ConfigurationViewModel
|
||||
protected lateinit var bugReporter: BugReporter
|
||||
private lateinit var rageShake: RageShake
|
||||
protected lateinit var navigator: Navigator
|
||||
|
||||
private var unBinder: Unbinder? = null
|
||||
|
||||
@ -121,6 +124,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
|
||||
configurationViewModel = ViewModelProviders.of(this, viewModelFactory).get(ConfigurationViewModel::class.java)
|
||||
bugReporter = screenComponent.bugReporter()
|
||||
rageShake = screenComponent.rageShake()
|
||||
navigator = screenComponent.navigator()
|
||||
configurationViewModel.activityRestarter.observe(this, Observer {
|
||||
if (!it.hasBeenHandled) {
|
||||
// Recreate the Activity because configuration has changed
|
||||
@ -262,6 +266,24 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
protected fun recursivelyDispatchOnBackPressed(fm: FragmentManager): Boolean {
|
||||
// if (fm.backStackEntryCount == 0)
|
||||
// return false
|
||||
|
||||
val reverseOrder = fm.fragments.filter { it is OnBackPressed }.reversed()
|
||||
for (f in reverseOrder) {
|
||||
val handledByChildFragments = recursivelyDispatchOnBackPressed(f.childFragmentManager)
|
||||
if (handledByChildFragments) {
|
||||
return true
|
||||
}
|
||||
val backPressable = f as OnBackPressed
|
||||
if (backPressable.onBackPressed()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* PROTECTED METHODS
|
||||
* ========================================================================================== */
|
||||
|
@ -65,7 +65,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed, HasScreen
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
|
||||
navigator = vectorBaseActivity.getVectorComponent().navigator()
|
||||
navigator = screenComponent.navigator()
|
||||
viewModelFactory = screenComponent.viewModelFactory()
|
||||
injectWith(injector())
|
||||
super.onAttach(context)
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.riotx.core.utils
|
||||
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.functions.Consumer
|
||||
import io.reactivex.internal.functions.Functions
|
||||
import timber.log.Timber
|
||||
|
||||
fun <T> Single<T>.subscribeLogError(): Disposable {
|
||||
return subscribe(Functions.emptyConsumer(), Consumer { Timber.e(it) })
|
||||
}
|
||||
|
||||
fun Completable.subscribeLogError(): Disposable {
|
||||
return subscribe({}, { Timber.e(it) })
|
||||
}
|
@ -43,6 +43,7 @@ class KeysBackupManageActivity : SimpleFragmentActivity() {
|
||||
@Inject lateinit var keysBackupSettingsViewModelFactory: KeysBackupSettingsViewModel.Factory
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
super.injectWith(injector)
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
@Inject lateinit var homeActivityViewModelFactory: HomeActivityViewModel.Factory
|
||||
@Inject lateinit var homeNavigator: HomeNavigator
|
||||
@Inject lateinit var navigator: Navigator
|
||||
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
||||
@Inject lateinit var pushManager: PushersManager
|
||||
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
||||
@ -214,23 +213,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
}
|
||||
}
|
||||
|
||||
private fun recursivelyDispatchOnBackPressed(fm: FragmentManager): Boolean {
|
||||
// if (fm.backStackEntryCount == 0)
|
||||
// return false
|
||||
|
||||
val reverseOrder = fm.fragments.filter { it is OnBackPressed }.reversed()
|
||||
for (f in reverseOrder) {
|
||||
val handledByChildFragments = recursivelyDispatchOnBackPressed(f.childFragmentManager)
|
||||
if (handledByChildFragments) {
|
||||
return true
|
||||
}
|
||||
val backPressable = f as OnBackPressed
|
||||
if (backPressable.onBackPressed()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
@ -16,11 +16,15 @@
|
||||
|
||||
package im.vector.riotx.features.home.createdirect
|
||||
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
|
||||
sealed class CreateDirectRoomActions {
|
||||
|
||||
object CreateRoomAndInviteSelectedUsers : CreateDirectRoomActions()
|
||||
data class FilterKnownUsers(val value: String) : CreateDirectRoomActions()
|
||||
object ClearFilterKnownUsers: CreateDirectRoomActions()
|
||||
object SelectAddByMatrixId : CreateDirectRoomActions()
|
||||
data class SearchDirectoryUsers(val value: String) : CreateDirectRoomActions()
|
||||
object ClearFilterKnownUsers : CreateDirectRoomActions()
|
||||
data class SelectUser(val user: User) : CreateDirectRoomActions()
|
||||
data class RemoveSelectedUser(val user: User) : CreateDirectRoomActions()
|
||||
|
||||
}
|
||||
|
@ -20,20 +20,84 @@ package im.vector.riotx.features.home.createdirect
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.viewModel
|
||||
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ScreenComponent
|
||||
import im.vector.riotx.core.extensions.addFragment
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.extensions.addFragmentToBackstack
|
||||
import im.vector.riotx.core.extensions.observeEvent
|
||||
import im.vector.riotx.core.platform.SimpleFragmentActivity
|
||||
import im.vector.riotx.core.platform.WaitingViewData
|
||||
import kotlinx.android.synthetic.main.activity.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreateDirectRoomActivity : VectorBaseActivity() {
|
||||
class CreateDirectRoomActivity : SimpleFragmentActivity() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.activity_simple
|
||||
sealed class Navigation {
|
||||
object UsersDirectory : Navigation()
|
||||
object Close : Navigation()
|
||||
object Previous : Navigation()
|
||||
}
|
||||
|
||||
override fun initUiAndData() {
|
||||
private val viewModel: CreateDirectRoomViewModel by viewModel()
|
||||
lateinit var navigationViewModel: CreateDirectRoomNavigationViewModel
|
||||
@Inject lateinit var createDirectRoomViewModelFactory: CreateDirectRoomViewModel.Factory
|
||||
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
super.injectWith(injector)
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
toolbar.visibility = View.GONE
|
||||
navigationViewModel = ViewModelProviders.of(this, viewModelFactory).get(CreateDirectRoomNavigationViewModel::class.java)
|
||||
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
|
||||
when (navigation) {
|
||||
is Navigation.UsersDirectory -> addFragmentToBackstack(CreateDirectRoomDirectoryUsersFragment(), R.id.container)
|
||||
Navigation.Close -> finish()
|
||||
Navigation.Previous -> onBackPressed()
|
||||
}
|
||||
}
|
||||
if (isFirstCreation()) {
|
||||
addFragment(CreateDirectRoomFragment(), R.id.simpleFragmentContainer)
|
||||
addFragment(CreateDirectRoomFragment(), R.id.container)
|
||||
}
|
||||
viewModel.subscribe(this) { renderState(it) }
|
||||
}
|
||||
|
||||
private fun renderState(state: CreateDirectRoomViewState) {
|
||||
when (state.createAndInviteState) {
|
||||
is Loading -> renderCreationLoading()
|
||||
is Success -> renderCreationSuccess(state.createAndInviteState())
|
||||
is Fail -> renderCreationFailure(state.createAndInviteState.error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderCreationLoading() {
|
||||
updateWaitingView(WaitingViewData(getString(R.string.room_recents_create_room)))
|
||||
}
|
||||
|
||||
private fun renderCreationFailure(error: Throwable) {
|
||||
|
||||
}
|
||||
|
||||
private fun renderCreationSuccess(roomId: String?) {
|
||||
// Navigate to freshly created room
|
||||
if (roomId != null) {
|
||||
navigator.openRoom(this, roomId)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context): Intent {
|
||||
return Intent(context, CreateDirectRoomActivity::class.java)
|
||||
|
@ -19,14 +19,27 @@
|
||||
package im.vector.riotx.features.home.createdirect
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import com.airbnb.epoxy.VisibilityState
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Incomplete
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.android.internal.util.firstLetterOfDisplayName
|
||||
import im.vector.riotx.core.epoxy.errorWithRetryItem
|
||||
import im.vector.riotx.core.epoxy.loadingItem
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreateDirectRoomController @Inject constructor(private val avatarRenderer: AvatarRenderer) : EpoxyController() {
|
||||
class CreateDirectRoomController @Inject constructor(private val avatarRenderer: AvatarRenderer,
|
||||
private val errorFormatter: ErrorFormatter) : EpoxyController() {
|
||||
|
||||
private var state: CreateDirectRoomViewState? = null
|
||||
var displayMode = CreateDirectRoomViewState.DisplayMode.KNOWN_USERS
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
init {
|
||||
@ -40,10 +53,36 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer:
|
||||
|
||||
override fun buildModels() {
|
||||
val currentState = state ?: return
|
||||
val knownUsers = currentState.knownUsers() ?: return
|
||||
val asyncUsers = if (displayMode == CreateDirectRoomViewState.DisplayMode.DIRECTORY_USERS) {
|
||||
currentState.directoryUsers
|
||||
} else {
|
||||
currentState.knownUsers
|
||||
}
|
||||
when (asyncUsers) {
|
||||
is Incomplete -> renderLoading()
|
||||
is Success -> renderUsers(asyncUsers(), currentState.selectedUsers)
|
||||
is Fail -> renderFailure(asyncUsers.error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderLoading() {
|
||||
loadingItem {
|
||||
id("loading")
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderFailure(failure: Throwable) {
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
text(errorFormatter.toHumanReadable(failure))
|
||||
listener { callback?.retryDirectoryUsersRequest() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderUsers(users: List<User>, selectedUsers: Set<User>) {
|
||||
var lastFirstLetter: String? = null
|
||||
knownUsers.forEach { user ->
|
||||
users.forEach { user ->
|
||||
val isSelected = selectedUsers.contains(user)
|
||||
val currentFirstLetter = user.displayName.firstLetterOfDisplayName()
|
||||
val showLetter = currentFirstLetter.isNotEmpty() && lastFirstLetter != currentFirstLetter
|
||||
lastFirstLetter = currentFirstLetter
|
||||
@ -55,6 +94,7 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer:
|
||||
|
||||
createDirectRoomUserItem {
|
||||
id(user.userId)
|
||||
selected(isSelected)
|
||||
userId(user.userId)
|
||||
name(user.displayName)
|
||||
avatarUrl(user.avatarUrl)
|
||||
@ -64,11 +104,13 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onItemClick(user: User)
|
||||
fun retryDirectoryUsersRequest() {
|
||||
// NO-OP
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.riotx.features.home.createdirect
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.jakewharton.rxbinding3.widget.textChanges
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ScreenComponent
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreateDirectRoomDirectoryUsersFragment : VectorBaseFragment(), CreateDirectRoomController.Callback {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_create_direct_room_directory_users
|
||||
|
||||
private val viewModel: CreateDirectRoomViewModel by activityViewModel()
|
||||
|
||||
@Inject lateinit var directRoomController: CreateDirectRoomController
|
||||
private lateinit var navigationViewModel: CreateDirectRoomNavigationViewModel
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
navigationViewModel = ViewModelProviders.of(requireActivity(), viewModelFactory).get(CreateDirectRoomNavigationViewModel::class.java)
|
||||
setupRecyclerView()
|
||||
setupSearchByMatrixIdView()
|
||||
setupCloseView()
|
||||
viewModel.subscribe(this) { renderState(it) }
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
recyclerView.setHasFixedSize(true)
|
||||
directRoomController.callback = this
|
||||
directRoomController.displayMode = CreateDirectRoomViewState.DisplayMode.DIRECTORY_USERS
|
||||
recyclerView.setController(directRoomController)
|
||||
}
|
||||
|
||||
private fun setupSearchByMatrixIdView() {
|
||||
createDirectRoomSearchById
|
||||
.textChanges()
|
||||
.subscribe {
|
||||
viewModel.handle(CreateDirectRoomActions.SearchDirectoryUsers(it.toString()))
|
||||
}
|
||||
.disposeOnDestroy()
|
||||
createDirectRoomSearchById.requestFocus()
|
||||
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
imm?.showSoftInput(createDirectRoomSearchById, InputMethodManager.SHOW_IMPLICIT)
|
||||
|
||||
}
|
||||
|
||||
private fun setupCloseView() {
|
||||
createDirectRoomClose.setOnClickListener {
|
||||
navigationViewModel.goTo(CreateDirectRoomActivity.Navigation.Close)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderState(state: CreateDirectRoomViewState) {
|
||||
directRoomController.setData(state)
|
||||
}
|
||||
|
||||
override fun onItemClick(user: User) {
|
||||
viewModel.handle(CreateDirectRoomActions.SelectUser(user))
|
||||
navigationViewModel.goTo(CreateDirectRoomActivity.Navigation.Previous)
|
||||
}
|
||||
|
||||
override fun retryDirectoryUsersRequest() {
|
||||
val currentSearch = createDirectRoomSearchById.text.toString()
|
||||
viewModel.handle(CreateDirectRoomActions.SearchDirectoryUsers(currentSearch))
|
||||
}
|
||||
}
|
@ -20,14 +20,20 @@ package im.vector.riotx.features.home.createdirect
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ScreenComponent
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
||||
import kotlinx.android.synthetic.main.fragment_create_direct_room.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.android.synthetic.main.fragment_public_rooms.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomController.Callback {
|
||||
@ -36,10 +42,10 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle
|
||||
|
||||
override fun getMenuRes() = R.menu.vector_create_direct_room
|
||||
|
||||
private val viewModel: CreateDirectRoomViewModel by fragmentViewModel()
|
||||
private val viewModel: CreateDirectRoomViewModel by activityViewModel()
|
||||
|
||||
@Inject lateinit var createDirectRoomViewModelFactory: CreateDirectRoomViewModel.Factory
|
||||
@Inject lateinit var directRoomController: CreateDirectRoomController
|
||||
private lateinit var navigationViewModel: CreateDirectRoomNavigationViewModel
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
@ -47,14 +53,18 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
navigationViewModel = ViewModelProviders.of(requireActivity(), viewModelFactory).get(CreateDirectRoomNavigationViewModel::class.java)
|
||||
vectorBaseActivity.setSupportActionBar(createDirectRoomToolbar)
|
||||
setupRecyclerView()
|
||||
setupFilterView()
|
||||
setupAddByMatrixIdView()
|
||||
setupCloseView()
|
||||
viewModel.subscribe(this) { renderState(it) }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.action_create_room -> {
|
||||
R.id.action_create_direct_room -> {
|
||||
viewModel.handle(CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers)
|
||||
true
|
||||
}
|
||||
@ -63,11 +73,18 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAddByMatrixIdView() {
|
||||
addByMatrixId.setOnClickListener {
|
||||
navigationViewModel.goTo(CreateDirectRoomActivity.Navigation.UsersDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
recyclerView.setHasFixedSize(true)
|
||||
// Don't activate animation as we might have way to much item animation when filtering
|
||||
recyclerView.itemAnimator = null
|
||||
directRoomController.callback = this
|
||||
directRoomController.displayMode = CreateDirectRoomViewState.DisplayMode.KNOWN_USERS
|
||||
recyclerView.setController(directRoomController)
|
||||
}
|
||||
|
||||
@ -85,11 +102,18 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle
|
||||
.disposeOnDestroy()
|
||||
}
|
||||
|
||||
private fun setupCloseView() {
|
||||
createDirectRoomClose.setOnClickListener {
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderState(state: CreateDirectRoomViewState) {
|
||||
|
||||
directRoomController.setData(state)
|
||||
}
|
||||
|
||||
override fun onItemClick(user: User) {
|
||||
vectorBaseActivity.notImplemented("IMPLEMENT ON USER CLICKED")
|
||||
viewModel.handle(CreateDirectRoomActions.SelectUser(user))
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.riotx.features.home.createdirect
|
||||
|
||||
import im.vector.riotx.core.mvrx.NavigationViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreateDirectRoomNavigationViewModel @Inject constructor(): NavigationViewModel<CreateDirectRoomActivity.Navigation>()
|
@ -21,12 +21,16 @@ package im.vector.riotx.features.home.createdirect
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import im.vector.riotx.features.home.getColorFromUserId
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_create_direct_room_user)
|
||||
abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserItem.Holder>() {
|
||||
@ -36,6 +40,7 @@ abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserI
|
||||
@EpoxyAttribute var userId: String = ""
|
||||
@EpoxyAttribute var avatarUrl: String? = null
|
||||
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
||||
@EpoxyAttribute var selected: Boolean = false
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.view.setOnClickListener(clickListener)
|
||||
@ -48,13 +53,22 @@ abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserI
|
||||
holder.nameView.text = name
|
||||
holder.userIdView.text = userId
|
||||
}
|
||||
if (selected) {
|
||||
holder.avatarCheckedImageView.visibility = View.VISIBLE
|
||||
val backgroundColor = ContextCompat.getColor(holder.view.context, R.color.riotx_accent)
|
||||
val backgroundDrawable = TextDrawable.builder().buildRound("", backgroundColor)
|
||||
holder.avatarImageView.setImageDrawable(backgroundDrawable)
|
||||
} else {
|
||||
holder.avatarCheckedImageView.visibility = View.GONE
|
||||
avatarRenderer.render(avatarUrl, userId, name, holder.avatarImageView)
|
||||
}
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val userIdView by bind<TextView>(R.id.createDirectRoomUserID)
|
||||
val nameView by bind<TextView>(R.id.createDirectRoomUserName)
|
||||
val avatarImageView by bind<ImageView>(R.id.createDirectRoomUserAvatar)
|
||||
val avatarCheckedImageView by bind<ImageView>(R.id.createDirectRoomUserAvatarChecked)
|
||||
}
|
||||
|
||||
}
|
@ -19,22 +19,23 @@
|
||||
package im.vector.riotx.features.home.createdirect
|
||||
|
||||
import arrow.core.Option
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||
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.room.model.create.CreateRoomParams
|
||||
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
|
||||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private typealias KnowUsersFilter = String
|
||||
private typealias DirectoryUsersSearch = String
|
||||
|
||||
class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
|
||||
initialState: CreateDirectRoomViewState,
|
||||
@ -47,35 +48,77 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
|
||||
}
|
||||
|
||||
private val knownUsersFilter = BehaviorRelay.createDefault<Option<KnowUsersFilter>>(Option.empty())
|
||||
private val directoryUsersSearch = BehaviorRelay.create<DirectoryUsersSearch>()
|
||||
|
||||
companion object : MvRxViewModelFactory<CreateDirectRoomViewModel, CreateDirectRoomViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: CreateDirectRoomViewState): CreateDirectRoomViewModel? {
|
||||
val fragment: CreateDirectRoomFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.createDirectRoomViewModelFactory.create(state)
|
||||
val activity: CreateDirectRoomActivity = (viewModelContext as ActivityViewModelContext).activity()
|
||||
return activity.createDirectRoomViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
observeKnownUsers()
|
||||
observeDirectoryUsers()
|
||||
}
|
||||
|
||||
fun handle(createDirectRoomActions: CreateDirectRoomActions) {
|
||||
when (createDirectRoomActions) {
|
||||
fun handle(action: CreateDirectRoomActions) {
|
||||
when (action) {
|
||||
is CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers -> createRoomAndInviteSelectedUsers()
|
||||
is CreateDirectRoomActions.SelectAddByMatrixId -> handleSelectAddByMatrixId()
|
||||
is CreateDirectRoomActions.FilterKnownUsers -> knownUsersFilter.accept(Option.just(createDirectRoomActions.value))
|
||||
is CreateDirectRoomActions.FilterKnownUsers -> knownUsersFilter.accept(Option.just(action.value))
|
||||
is CreateDirectRoomActions.ClearFilterKnownUsers -> knownUsersFilter.accept(Option.empty())
|
||||
is CreateDirectRoomActions.SearchDirectoryUsers -> directoryUsersSearch.accept(action.value)
|
||||
is CreateDirectRoomActions.SelectUser -> handleSelectUser(action)
|
||||
is CreateDirectRoomActions.RemoveSelectedUser -> handleRemoveSelectedUser(action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSelectAddByMatrixId() {
|
||||
// TODO
|
||||
private fun createRoomAndInviteSelectedUsers() = withState {
|
||||
val isDirect = it.selectedUsers.size == 1
|
||||
val roomParams = CreateRoomParams().apply {
|
||||
invitedUserIds = ArrayList(it.selectedUsers.map { user -> user.userId })
|
||||
if (isDirect) {
|
||||
setDirectMessage()
|
||||
}
|
||||
}
|
||||
session.rx()
|
||||
.createRoom(roomParams)
|
||||
.execute {
|
||||
copy(createAndInviteState = it)
|
||||
}
|
||||
.disposeOnClear()
|
||||
}
|
||||
|
||||
private fun handleRemoveSelectedUser(action: CreateDirectRoomActions.RemoveSelectedUser) = withState {
|
||||
val selectedUsers = it.selectedUsers.minusElement(action.user)
|
||||
setState { copy(selectedUsers = selectedUsers) }
|
||||
}
|
||||
|
||||
private fun handleSelectUser(action: CreateDirectRoomActions.SelectUser) = withState {
|
||||
val selectedUsers = if (it.selectedUsers.contains(action.user)) {
|
||||
it.selectedUsers.minusElement(action.user)
|
||||
} else {
|
||||
it.selectedUsers.plus(action.user)
|
||||
}
|
||||
setState { copy(selectedUsers = selectedUsers) }
|
||||
}
|
||||
|
||||
private fun observeDirectoryUsers() {
|
||||
directoryUsersSearch
|
||||
.throttleLast(300, TimeUnit.MILLISECONDS)
|
||||
.switchMapSingle { search ->
|
||||
session.rx()
|
||||
.searchUsersDirectory(search, 50, emptySet())
|
||||
.map { users ->
|
||||
users.sortedBy { it.displayName }
|
||||
}
|
||||
}
|
||||
.execute { async ->
|
||||
copy(directoryUsers = async)
|
||||
}
|
||||
|
||||
private fun createRoomAndInviteSelectedUsers() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private fun observeKnownUsers() {
|
||||
|
@ -24,14 +24,15 @@ import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
|
||||
data class CreateDirectRoomViewState(
|
||||
val displayMode: DisplayMode = DisplayMode.KNOWN_USERS,
|
||||
val knownUsers: Async<List<User>> = Uninitialized,
|
||||
val filteredKnownUsers: Async<List<User>> = Uninitialized
|
||||
val directoryUsers: Async<List<User>> = Uninitialized,
|
||||
val selectedUsers: Set<User> = emptySet(),
|
||||
val createAndInviteState: Async<String> = Uninitialized
|
||||
) : MvRxState {
|
||||
|
||||
enum class DisplayMode {
|
||||
KNOWN_USERS,
|
||||
MATRIX_ID_USERS
|
||||
DIRECTORY_USERS
|
||||
}
|
||||
|
||||
}
|
@ -43,6 +43,7 @@ import im.vector.riotx.core.intent.getFilenameFromUri
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.core.resources.UserPreferencesProvider
|
||||
import im.vector.riotx.core.utils.LiveEvent
|
||||
import im.vector.riotx.core.utils.subscribeLogError
|
||||
import im.vector.riotx.features.command.CommandParser
|
||||
import im.vector.riotx.features.command.ParsedCommand
|
||||
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
||||
@ -94,7 +95,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||
observeRoomSummary()
|
||||
observeEventDisplayedActions()
|
||||
observeInvitationState()
|
||||
cancelableBag += room.loadRoomMembersIfNeeded()
|
||||
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
||||
timeline.start()
|
||||
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActiv
|
||||
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
||||
import im.vector.riotx.features.debug.DebugMenuActivity
|
||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity
|
||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomFragment
|
||||
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
|
||||
|
@ -10,7 +10,7 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/createRoomToolbar"
|
||||
android:id="@+id/createDirectRoomToolbar"
|
||||
style="@style/VectorToolbarStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="?actionBarSize"
|
||||
@ -27,6 +27,9 @@
|
||||
android:id="@+id/createDirectRoomClose"
|
||||
android:layout_width="@dimen/layout_touch_size"
|
||||
android:layout_height="@dimen/layout_touch_size"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_x_18dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@ -59,16 +62,20 @@
|
||||
android:id="@+id/createDirectRoomFilterContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
app:cardElevation="4dp"
|
||||
app:cardUseCompatPadding="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/createRoomToolbar">
|
||||
app:layout_constraintTop_toBottomOf="@+id/createDirectRoomToolbar">
|
||||
|
||||
<androidx.appcompat.widget.SearchView
|
||||
android:id="@+id/createDirectRoomFilter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text|textMultiLine"
|
||||
app:closeIcon="@drawable/ic_x_green"
|
||||
app:iconifiedByDefault="false"
|
||||
app:queryBackground="@android:color/transparent"
|
||||
@ -88,6 +95,7 @@
|
||||
android:layout_marginBottom="8dp"
|
||||
android:minHeight="@dimen/layout_touch_size"
|
||||
android:text="@string/add_by_matrix_id"
|
||||
android:visibility="visible"
|
||||
app:icon="@drawable/ic_plus_circle"
|
||||
app:iconPadding="13dp"
|
||||
app:iconTint="@color/riotx_accent"
|
||||
|
@ -0,0 +1,100 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/createRoomToolbar"
|
||||
style="@style/VectorToolbarStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:elevation="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/createDirectRoomClose"
|
||||
android:layout_width="@dimen/layout_touch_size"
|
||||
android:layout_height="@dimen/layout_touch_size"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_x_18dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createDirectRoomTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/direct_chats_header"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/createDirectRoomClose"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/createDirectRoomSearchByIdContainer"
|
||||
style="@style/VectorTextInputLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/createRoomToolbar">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/createDirectRoomSearchById"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/add_by_matrix_id" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:fastScrollEnabled="true"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/createDirectRoomSearchByIdContainer"
|
||||
tools:listitem="@layout/item_create_direct_room_user" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@ -7,19 +7,34 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?riotx_background"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/createDirectRoomUserAvatarContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/createDirectRoomUserAvatar"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/createDirectRoomUserAvatarChecked"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_material_done"
|
||||
android:tint="@android:color/white"
|
||||
android:visibility="visible" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createDirectRoomUserName"
|
||||
android:layout_width="0dp"
|
||||
@ -34,7 +49,7 @@
|
||||
app:layout_constraintBottom_toTopOf="@+id/createDirectRoomUserID"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/createDirectRoomUserAvatar"
|
||||
app:layout_constraintStart_toEndOf="@+id/createDirectRoomUserAvatarContainer"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@tools:sample/full_names" />
|
||||
|
||||
|
@ -1,11 +1,9 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".features.roomdirectory.RoomDirectoryActivity">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_create_room"
|
||||
android:id="@+id/action_create_direct_room"
|
||||
android:title="@string/create_room_action_create"
|
||||
app:showAsAction="always" />
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user