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.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import io.reactivex.Completable
|
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
|
||||||
@ -53,6 +52,12 @@ class RxSession(private val session: Session) {
|
|||||||
session.createRoom(roomParams, MatrixCallbackSingle(it)).toSingle(it)
|
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 {
|
fun Session.rx(): RxSession {
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
package im.vector.matrix.android.api.session.user
|
package im.vector.matrix.android.api.session.user
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
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.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.
|
* 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?
|
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
|
* Observe a live user from a userId
|
||||||
* @param userId the userId to look for.
|
* @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.LiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import com.zhuinden.monarchy.Monarchy
|
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.UserService
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
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.RealmLiveData
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
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.UserEntity
|
||||||
import im.vector.matrix.android.internal.database.model.UserEntityFields
|
import im.vector.matrix.android.internal.database.model.UserEntityFields
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
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 im.vector.matrix.android.internal.util.fetchCopied
|
||||||
import javax.inject.Inject
|
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? {
|
override fun getUser(userId: String): User? {
|
||||||
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
|
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
|
||||||
@ -62,4 +67,15 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
|
|||||||
{ it.asDomain() }
|
{ 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.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
import im.vector.matrix.android.api.session.user.UserService
|
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
|
@Module
|
||||||
internal abstract class UserModule {
|
internal abstract class UserModule {
|
||||||
|
|
||||||
|
@Module
|
||||||
|
companion object {
|
||||||
|
@Provides
|
||||||
|
@JvmStatic
|
||||||
|
@SessionScope
|
||||||
|
fun providesSearchUserAPI(retrofit: Retrofit): SearchUserAPI {
|
||||||
|
return retrofit.create(SearchUserAPI::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindUserService(userService: DefaultUserService): UserService
|
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.HomeDetailFragment
|
||||||
import im.vector.riotx.features.home.HomeDrawerFragment
|
import im.vector.riotx.features.home.HomeDrawerFragment
|
||||||
import im.vector.riotx.features.home.HomeModule
|
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.createdirect.CreateDirectRoomFragment
|
||||||
import im.vector.riotx.features.home.group.GroupListFragment
|
import im.vector.riotx.features.home.group.GroupListFragment
|
||||||
import im.vector.riotx.features.home.room.detail.RoomDetailFragment
|
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.login.LoginActivity
|
||||||
import im.vector.riotx.features.media.ImageMediaViewerActivity
|
import im.vector.riotx.features.media.ImageMediaViewerActivity
|
||||||
import im.vector.riotx.features.media.VideoMediaViewerActivity
|
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.BugReportActivity
|
||||||
import im.vector.riotx.features.rageshake.BugReporter
|
import im.vector.riotx.features.rageshake.BugReporter
|
||||||
import im.vector.riotx.features.rageshake.RageShake
|
import im.vector.riotx.features.rageshake.RageShake
|
||||||
@ -74,6 +77,8 @@ interface ScreenComponent {
|
|||||||
|
|
||||||
fun rageShake(): RageShake
|
fun rageShake(): RageShake
|
||||||
|
|
||||||
|
fun navigator(): Navigator
|
||||||
|
|
||||||
fun inject(activity: HomeActivity)
|
fun inject(activity: HomeActivity)
|
||||||
|
|
||||||
fun inject(roomDetailFragment: RoomDetailFragment)
|
fun inject(roomDetailFragment: RoomDetailFragment)
|
||||||
@ -154,7 +159,11 @@ interface ScreenComponent {
|
|||||||
|
|
||||||
fun inject(pushGatewaysFragment: PushGatewaysFragment)
|
fun inject(pushGatewaysFragment: PushGatewaysFragment)
|
||||||
|
|
||||||
fun inject(createDirectRoomFragment: CreateDirectRoomFragment)
|
fun inject(createDirectRoomKnownUsersFragment: CreateDirectRoomFragment)
|
||||||
|
|
||||||
|
fun inject(createDirectRoomDirectoryUsersFragment: CreateDirectRoomDirectoryUsersFragment)
|
||||||
|
|
||||||
|
fun inject(createDirectRoomActivity: CreateDirectRoomActivity)
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface 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.keysbackup.setup.KeysBackupSetupSharedViewModel
|
||||||
import im.vector.riotx.features.crypto.verification.SasVerificationViewModel
|
import im.vector.riotx.features.crypto.verification.SasVerificationViewModel
|
||||||
import im.vector.riotx.features.home.*
|
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
|
||||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomViewModel_AssistedFactory
|
import im.vector.riotx.features.home.createdirect.CreateDirectRoomViewModel_AssistedFactory
|
||||||
import im.vector.riotx.features.home.group.GroupListViewModel
|
import im.vector.riotx.features.home.group.GroupListViewModel
|
||||||
@ -118,6 +119,11 @@ interface ViewModelModule {
|
|||||||
@ViewModelKey(ConfigurationViewModel::class)
|
@ViewModelKey(ConfigurationViewModel::class)
|
||||||
fun bindConfigurationViewModel(viewModel: ConfigurationViewModel): ViewModel
|
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.
|
* 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.view.View
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
@ -46,6 +47,7 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() {
|
|||||||
|
|
||||||
@Inject lateinit var session: Session
|
@Inject lateinit var session: Session
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
override fun injectWith(injector: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
session = injector.session()
|
session = injector.session()
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import androidx.annotation.*
|
|||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
@ -40,6 +41,7 @@ import im.vector.riotx.R
|
|||||||
import im.vector.riotx.core.di.*
|
import im.vector.riotx.core.di.*
|
||||||
import im.vector.riotx.core.utils.toast
|
import im.vector.riotx.core.utils.toast
|
||||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
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.BugReportActivity
|
||||||
import im.vector.riotx.features.rageshake.BugReporter
|
import im.vector.riotx.features.rageshake.BugReporter
|
||||||
import im.vector.riotx.features.rageshake.RageShake
|
import im.vector.riotx.features.rageshake.RageShake
|
||||||
@ -70,6 +72,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
|
|||||||
private lateinit var configurationViewModel: ConfigurationViewModel
|
private lateinit var configurationViewModel: ConfigurationViewModel
|
||||||
protected lateinit var bugReporter: BugReporter
|
protected lateinit var bugReporter: BugReporter
|
||||||
private lateinit var rageShake: RageShake
|
private lateinit var rageShake: RageShake
|
||||||
|
protected lateinit var navigator: Navigator
|
||||||
|
|
||||||
private var unBinder: Unbinder? = null
|
private var unBinder: Unbinder? = null
|
||||||
|
|
||||||
@ -121,6 +124,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
|
|||||||
configurationViewModel = ViewModelProviders.of(this, viewModelFactory).get(ConfigurationViewModel::class.java)
|
configurationViewModel = ViewModelProviders.of(this, viewModelFactory).get(ConfigurationViewModel::class.java)
|
||||||
bugReporter = screenComponent.bugReporter()
|
bugReporter = screenComponent.bugReporter()
|
||||||
rageShake = screenComponent.rageShake()
|
rageShake = screenComponent.rageShake()
|
||||||
|
navigator = screenComponent.navigator()
|
||||||
configurationViewModel.activityRestarter.observe(this, Observer {
|
configurationViewModel.activityRestarter.observe(this, Observer {
|
||||||
if (!it.hasBeenHandled) {
|
if (!it.hasBeenHandled) {
|
||||||
// Recreate the Activity because configuration has changed
|
// Recreate the Activity because configuration has changed
|
||||||
@ -262,6 +266,24 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
|
|||||||
return super.onOptionsItemSelected(item)
|
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
|
* PROTECTED METHODS
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
@ -65,7 +65,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed, HasScreen
|
|||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
|
screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
|
||||||
navigator = vectorBaseActivity.getVectorComponent().navigator()
|
navigator = screenComponent.navigator()
|
||||||
viewModelFactory = screenComponent.viewModelFactory()
|
viewModelFactory = screenComponent.viewModelFactory()
|
||||||
injectWith(injector())
|
injectWith(injector())
|
||||||
super.onAttach(context)
|
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
|
@Inject lateinit var keysBackupSettingsViewModelFactory: KeysBackupSettingsViewModel.Factory
|
||||||
|
|
||||||
override fun injectWith(injector: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
|
super.injectWith(injector)
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
@Inject lateinit var homeActivityViewModelFactory: HomeActivityViewModel.Factory
|
@Inject lateinit var homeActivityViewModelFactory: HomeActivityViewModel.Factory
|
||||||
@Inject lateinit var homeNavigator: HomeNavigator
|
@Inject lateinit var homeNavigator: HomeNavigator
|
||||||
@Inject lateinit var navigator: Navigator
|
|
||||||
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
||||||
@Inject lateinit var pushManager: PushersManager
|
@Inject lateinit var pushManager: PushersManager
|
||||||
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
@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 {
|
companion object {
|
||||||
|
@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package im.vector.riotx.features.home.createdirect
|
package im.vector.riotx.features.home.createdirect
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
|
|
||||||
sealed class CreateDirectRoomActions {
|
sealed class CreateDirectRoomActions {
|
||||||
|
|
||||||
object CreateRoomAndInviteSelectedUsers : CreateDirectRoomActions()
|
object CreateRoomAndInviteSelectedUsers : CreateDirectRoomActions()
|
||||||
data class FilterKnownUsers(val value: String) : CreateDirectRoomActions()
|
data class FilterKnownUsers(val value: String) : CreateDirectRoomActions()
|
||||||
object ClearFilterKnownUsers: CreateDirectRoomActions()
|
data class SearchDirectoryUsers(val value: String) : CreateDirectRoomActions()
|
||||||
object SelectAddByMatrixId : 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.Context
|
||||||
import android.content.Intent
|
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.R
|
||||||
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.core.extensions.addFragment
|
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()) {
|
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 {
|
companion object {
|
||||||
fun getIntent(context: Context): Intent {
|
fun getIntent(context: Context): Intent {
|
||||||
return Intent(context, CreateDirectRoomActivity::class.java)
|
return Intent(context, CreateDirectRoomActivity::class.java)
|
||||||
|
@ -19,14 +19,27 @@
|
|||||||
package im.vector.riotx.features.home.createdirect
|
package im.vector.riotx.features.home.createdirect
|
||||||
|
|
||||||
import com.airbnb.epoxy.EpoxyController
|
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.api.session.user.model.User
|
||||||
import im.vector.matrix.android.internal.util.firstLetterOfDisplayName
|
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 im.vector.riotx.features.home.AvatarRenderer
|
||||||
import javax.inject.Inject
|
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
|
private var state: CreateDirectRoomViewState? = null
|
||||||
|
var displayMode = CreateDirectRoomViewState.DisplayMode.KNOWN_USERS
|
||||||
|
|
||||||
var callback: Callback? = null
|
var callback: Callback? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -40,10 +53,36 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer:
|
|||||||
|
|
||||||
override fun buildModels() {
|
override fun buildModels() {
|
||||||
val currentState = state ?: return
|
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
|
var lastFirstLetter: String? = null
|
||||||
knownUsers.forEach { user ->
|
users.forEach { user ->
|
||||||
|
val isSelected = selectedUsers.contains(user)
|
||||||
val currentFirstLetter = user.displayName.firstLetterOfDisplayName()
|
val currentFirstLetter = user.displayName.firstLetterOfDisplayName()
|
||||||
val showLetter = currentFirstLetter.isNotEmpty() && lastFirstLetter != currentFirstLetter
|
val showLetter = currentFirstLetter.isNotEmpty() && lastFirstLetter != currentFirstLetter
|
||||||
lastFirstLetter = currentFirstLetter
|
lastFirstLetter = currentFirstLetter
|
||||||
@ -55,6 +94,7 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer:
|
|||||||
|
|
||||||
createDirectRoomUserItem {
|
createDirectRoomUserItem {
|
||||||
id(user.userId)
|
id(user.userId)
|
||||||
|
selected(isSelected)
|
||||||
userId(user.userId)
|
userId(user.userId)
|
||||||
name(user.displayName)
|
name(user.displayName)
|
||||||
avatarUrl(user.avatarUrl)
|
avatarUrl(user.avatarUrl)
|
||||||
@ -64,11 +104,13 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
fun onItemClick(user: User)
|
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.os.Bundle
|
||||||
import android.view.MenuItem
|
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 com.jakewharton.rxbinding3.appcompat.queryTextChanges
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
||||||
import kotlinx.android.synthetic.main.fragment_create_direct_room.*
|
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
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomController.Callback {
|
class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomController.Callback {
|
||||||
@ -36,10 +42,10 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle
|
|||||||
|
|
||||||
override fun getMenuRes() = R.menu.vector_create_direct_room
|
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
|
@Inject lateinit var directRoomController: CreateDirectRoomController
|
||||||
|
private lateinit var navigationViewModel: CreateDirectRoomNavigationViewModel
|
||||||
|
|
||||||
override fun injectWith(injector: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
@ -47,27 +53,38 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
navigationViewModel = ViewModelProviders.of(requireActivity(), viewModelFactory).get(CreateDirectRoomNavigationViewModel::class.java)
|
||||||
|
vectorBaseActivity.setSupportActionBar(createDirectRoomToolbar)
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
setupFilterView()
|
setupFilterView()
|
||||||
|
setupAddByMatrixIdView()
|
||||||
|
setupCloseView()
|
||||||
viewModel.subscribe(this) { renderState(it) }
|
viewModel.subscribe(this) { renderState(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.action_create_room -> {
|
R.id.action_create_direct_room -> {
|
||||||
viewModel.handle(CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers)
|
viewModel.handle(CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else ->
|
else ->
|
||||||
super.onOptionsItemSelected(item)
|
super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupAddByMatrixIdView() {
|
||||||
|
addByMatrixId.setOnClickListener {
|
||||||
|
navigationViewModel.goTo(CreateDirectRoomActivity.Navigation.UsersDirectory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
// Don't activate animation as we might have way to much item animation when filtering
|
// Don't activate animation as we might have way to much item animation when filtering
|
||||||
recyclerView.itemAnimator = null
|
recyclerView.itemAnimator = null
|
||||||
directRoomController.callback = this
|
directRoomController.callback = this
|
||||||
|
directRoomController.displayMode = CreateDirectRoomViewState.DisplayMode.KNOWN_USERS
|
||||||
recyclerView.setController(directRoomController)
|
recyclerView.setController(directRoomController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,11 +102,18 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle
|
|||||||
.disposeOnDestroy()
|
.disposeOnDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupCloseView() {
|
||||||
|
createDirectRoomClose.setOnClickListener {
|
||||||
|
requireActivity().finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderState(state: CreateDirectRoomViewState) {
|
private fun renderState(state: CreateDirectRoomViewState) {
|
||||||
|
|
||||||
directRoomController.setData(state)
|
directRoomController.setData(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClick(user: User) {
|
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.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
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.AvatarRenderer
|
||||||
|
import im.vector.riotx.features.home.getColorFromUserId
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_create_direct_room_user)
|
@EpoxyModelClass(layout = R.layout.item_create_direct_room_user)
|
||||||
abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserItem.Holder>() {
|
abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserItem.Holder>() {
|
||||||
@ -36,6 +40,7 @@ abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserI
|
|||||||
@EpoxyAttribute var userId: String = ""
|
@EpoxyAttribute var userId: String = ""
|
||||||
@EpoxyAttribute var avatarUrl: String? = null
|
@EpoxyAttribute var avatarUrl: String? = null
|
||||||
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
||||||
|
@EpoxyAttribute var selected: Boolean = false
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
holder.view.setOnClickListener(clickListener)
|
holder.view.setOnClickListener(clickListener)
|
||||||
@ -48,13 +53,22 @@ abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserI
|
|||||||
holder.nameView.text = name
|
holder.nameView.text = name
|
||||||
holder.userIdView.text = userId
|
holder.userIdView.text = userId
|
||||||
}
|
}
|
||||||
avatarRenderer.render(avatarUrl, userId, name, holder.avatarImageView)
|
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() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
val userIdView by bind<TextView>(R.id.createDirectRoomUserID)
|
val userIdView by bind<TextView>(R.id.createDirectRoomUserID)
|
||||||
val nameView by bind<TextView>(R.id.createDirectRoomUserName)
|
val nameView by bind<TextView>(R.id.createDirectRoomUserName)
|
||||||
val avatarImageView by bind<ImageView>(R.id.createDirectRoomUserAvatar)
|
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
|
package im.vector.riotx.features.home.createdirect
|
||||||
|
|
||||||
import arrow.core.Option
|
import arrow.core.Option
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.ActivityViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.session.Session
|
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.android.api.session.user.model.User
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.functions.BiFunction
|
import io.reactivex.functions.BiFunction
|
||||||
import io.reactivex.subjects.BehaviorSubject
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
private typealias KnowUsersFilter = String
|
private typealias KnowUsersFilter = String
|
||||||
|
private typealias DirectoryUsersSearch = String
|
||||||
|
|
||||||
class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
|
class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
|
||||||
initialState: CreateDirectRoomViewState,
|
initialState: CreateDirectRoomViewState,
|
||||||
@ -47,35 +48,77 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val knownUsersFilter = BehaviorRelay.createDefault<Option<KnowUsersFilter>>(Option.empty())
|
private val knownUsersFilter = BehaviorRelay.createDefault<Option<KnowUsersFilter>>(Option.empty())
|
||||||
|
private val directoryUsersSearch = BehaviorRelay.create<DirectoryUsersSearch>()
|
||||||
|
|
||||||
companion object : MvRxViewModelFactory<CreateDirectRoomViewModel, CreateDirectRoomViewState> {
|
companion object : MvRxViewModelFactory<CreateDirectRoomViewModel, CreateDirectRoomViewState> {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: CreateDirectRoomViewState): CreateDirectRoomViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: CreateDirectRoomViewState): CreateDirectRoomViewModel? {
|
||||||
val fragment: CreateDirectRoomFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
val activity: CreateDirectRoomActivity = (viewModelContext as ActivityViewModelContext).activity()
|
||||||
return fragment.createDirectRoomViewModelFactory.create(state)
|
return activity.createDirectRoomViewModelFactory.create(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeKnownUsers()
|
observeKnownUsers()
|
||||||
|
observeDirectoryUsers()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handle(createDirectRoomActions: CreateDirectRoomActions) {
|
fun handle(action: CreateDirectRoomActions) {
|
||||||
when (createDirectRoomActions) {
|
when (action) {
|
||||||
is CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers -> createRoomAndInviteSelectedUsers()
|
is CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers -> createRoomAndInviteSelectedUsers()
|
||||||
is CreateDirectRoomActions.SelectAddByMatrixId -> handleSelectAddByMatrixId()
|
is CreateDirectRoomActions.FilterKnownUsers -> knownUsersFilter.accept(Option.just(action.value))
|
||||||
is CreateDirectRoomActions.FilterKnownUsers -> knownUsersFilter.accept(Option.just(createDirectRoomActions.value))
|
|
||||||
is CreateDirectRoomActions.ClearFilterKnownUsers -> knownUsersFilter.accept(Option.empty())
|
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() {
|
private fun createRoomAndInviteSelectedUsers() = withState {
|
||||||
// TODO
|
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 createRoomAndInviteSelectedUsers() {
|
private fun handleRemoveSelectedUser(action: CreateDirectRoomActions.RemoveSelectedUser) = withState {
|
||||||
// TODO
|
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 observeKnownUsers() {
|
private fun observeKnownUsers() {
|
||||||
|
@ -24,14 +24,15 @@ import com.airbnb.mvrx.Uninitialized
|
|||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
|
|
||||||
data class CreateDirectRoomViewState(
|
data class CreateDirectRoomViewState(
|
||||||
val displayMode: DisplayMode = DisplayMode.KNOWN_USERS,
|
|
||||||
val knownUsers: Async<List<User>> = Uninitialized,
|
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 {
|
) : MvRxState {
|
||||||
|
|
||||||
enum class DisplayMode {
|
enum class DisplayMode {
|
||||||
KNOWN_USERS,
|
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.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.resources.UserPreferencesProvider
|
import im.vector.riotx.core.resources.UserPreferencesProvider
|
||||||
import im.vector.riotx.core.utils.LiveEvent
|
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.CommandParser
|
||||||
import im.vector.riotx.features.command.ParsedCommand
|
import im.vector.riotx.features.command.ParsedCommand
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
||||||
@ -94,7 +95,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
observeRoomSummary()
|
observeRoomSummary()
|
||||||
observeEventDisplayedActions()
|
observeEventDisplayedActions()
|
||||||
observeInvitationState()
|
observeInvitationState()
|
||||||
cancelableBag += room.loadRoomMembersIfNeeded()
|
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
||||||
timeline.start()
|
timeline.start()
|
||||||
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
|
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
|
||||||
}
|
}
|
||||||
@ -235,12 +236,12 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
} else {
|
} else {
|
||||||
val messageContent: MessageContent? =
|
val messageContent: MessageContent? =
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||||
val existingBody = messageContent?.body ?: ""
|
val existingBody = messageContent?.body ?: ""
|
||||||
if (existingBody != action.text) {
|
if (existingBody != action.text) {
|
||||||
room.editTextMessage(state.sendMode.timelineEvent.root.eventId
|
room.editTextMessage(state.sendMode.timelineEvent.root.eventId
|
||||||
?: "", messageContent?.type
|
?: "", messageContent?.type
|
||||||
?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
|
?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
|
||||||
} else {
|
} else {
|
||||||
Timber.w("Same message content, do not send edition")
|
Timber.w("Same message content, do not send edition")
|
||||||
}
|
}
|
||||||
@ -255,7 +256,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
is SendMode.QUOTE -> {
|
is SendMode.QUOTE -> {
|
||||||
val messageContent: MessageContent? =
|
val messageContent: MessageContent? =
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||||
val textMsg = messageContent?.body
|
val textMsg = messageContent?.body
|
||||||
|
|
||||||
val finalText = legacyRiotQuoteText(textMsg, action.text)
|
val finalText = legacyRiotQuoteText(textMsg, action.text)
|
||||||
|
@ -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.crypto.keysbackup.setup.KeysBackupSetupActivity
|
||||||
import im.vector.riotx.features.debug.DebugMenuActivity
|
import im.vector.riotx.features.debug.DebugMenuActivity
|
||||||
import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity
|
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.RoomDetailActivity
|
||||||
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
||||||
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
|
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/createRoomToolbar"
|
android:id="@+id/createDirectRoomToolbar"
|
||||||
style="@style/VectorToolbarStyle"
|
style="@style/VectorToolbarStyle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="?actionBarSize"
|
android:layout_height="?actionBarSize"
|
||||||
@ -27,6 +27,9 @@
|
|||||||
android:id="@+id/createDirectRoomClose"
|
android:id="@+id/createDirectRoomClose"
|
||||||
android:layout_width="@dimen/layout_touch_size"
|
android:layout_width="@dimen/layout_touch_size"
|
||||||
android:layout_height="@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:scaleType="center"
|
||||||
android:src="@drawable/ic_x_18dp"
|
android:src="@drawable/ic_x_18dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
@ -59,16 +62,20 @@
|
|||||||
android:id="@+id/createDirectRoomFilterContainer"
|
android:id="@+id/createDirectRoomFilterContainer"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
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:cardElevation="4dp"
|
||||||
app:cardUseCompatPadding="true"
|
app:cardUseCompatPadding="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/createRoomToolbar">
|
app:layout_constraintTop_toBottomOf="@+id/createDirectRoomToolbar">
|
||||||
|
|
||||||
<androidx.appcompat.widget.SearchView
|
<androidx.appcompat.widget.SearchView
|
||||||
android:id="@+id/createDirectRoomFilter"
|
android:id="@+id/createDirectRoomFilter"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text|textMultiLine"
|
||||||
app:closeIcon="@drawable/ic_x_green"
|
app:closeIcon="@drawable/ic_x_green"
|
||||||
app:iconifiedByDefault="false"
|
app:iconifiedByDefault="false"
|
||||||
app:queryBackground="@android:color/transparent"
|
app:queryBackground="@android:color/transparent"
|
||||||
@ -88,6 +95,7 @@
|
|||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:minHeight="@dimen/layout_touch_size"
|
android:minHeight="@dimen/layout_touch_size"
|
||||||
android:text="@string/add_by_matrix_id"
|
android:text="@string/add_by_matrix_id"
|
||||||
|
android:visibility="visible"
|
||||||
app:icon="@drawable/ic_plus_circle"
|
app:icon="@drawable/ic_plus_circle"
|
||||||
app:iconPadding="13dp"
|
app:iconPadding="13dp"
|
||||||
app:iconTint="@color/riotx_accent"
|
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,18 +7,33 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?riotx_background"
|
android:background="?riotx_background"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="8dp">
|
android:padding="8dp">
|
||||||
|
|
||||||
<ImageView
|
<FrameLayout
|
||||||
android:id="@+id/createDirectRoomUserAvatar"
|
android:id="@+id/createDirectRoomUserAvatarContainer"
|
||||||
android:layout_width="40dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="40dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
tools:src="@tools:sample/avatars" />
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/createDirectRoomUserAvatar"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
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
|
<TextView
|
||||||
android:id="@+id/createDirectRoomUserName"
|
android:id="@+id/createDirectRoomUserName"
|
||||||
@ -34,7 +49,7 @@
|
|||||||
app:layout_constraintBottom_toTopOf="@+id/createDirectRoomUserID"
|
app:layout_constraintBottom_toTopOf="@+id/createDirectRoomUserID"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
app:layout_constraintStart_toEndOf="@+id/createDirectRoomUserAvatar"
|
app:layout_constraintStart_toEndOf="@+id/createDirectRoomUserAvatarContainer"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="@tools:sample/full_names" />
|
tools:text="@tools:sample/full_names" />
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:context=".features.roomdirectory.RoomDirectoryActivity">
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_create_room"
|
android:id="@+id/action_create_direct_room"
|
||||||
android:title="@string/create_room_action_create"
|
android:title="@string/create_room_action_create"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user