Merge pull request #638 from vector-im/feature/filter

Fix 2 issues with share Activity: filter and room lists
This commit is contained in:
Benoit Marty 2019-10-24 14:03:08 +02:00 committed by GitHub
commit e52f0faaa7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 251 additions and 41 deletions

View file

@ -42,7 +42,7 @@ import javax.inject.Singleton
@Singleton
class AppStateHandler @Inject constructor(
private val sessionObservableStore: ActiveSessionObservableStore,
private val homeRoomListStore: HomeRoomListObservableStore,
private val homeRoomListObservableStore: HomeRoomListObservableStore,
private val selectedGroupStore: SelectedGroupStore) : LifecycleObserver {
private val compositeDisposable = CompositeDisposable()
@ -92,7 +92,7 @@ class AppStateHandler @Inject constructor(
}
)
.subscribe {
homeRoomListStore.post(it)
homeRoomListObservableStore.post(it)
}
.addTo(compositeDisposable)
}

View file

@ -41,11 +41,12 @@ import im.vector.riotx.features.home.createdirect.CreateDirectRoomKnownUsersFrag
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.readreceipts.DisplayReadReceiptsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.action.*
import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.home.room.list.RoomListModule
import im.vector.riotx.features.invite.VectorInviteView
import im.vector.riotx.features.link.LinkHandlerActivity
import im.vector.riotx.features.login.LoginActivity
@ -70,7 +71,17 @@ import im.vector.riotx.features.settings.push.PushGatewaysFragment
import im.vector.riotx.features.share.IncomingShareActivity
import im.vector.riotx.features.ui.UiStateRepository
@Component(dependencies = [VectorComponent::class], modules = [AssistedInjectModule::class, ViewModelModule::class, HomeModule::class])
@Component(
dependencies = [
VectorComponent::class
],
modules = [
AssistedInjectModule::class,
ViewModelModule::class,
HomeModule::class,
RoomListModule::class
]
)
@ScreenScope
interface ScreenComponent {

View file

@ -23,6 +23,7 @@ import dagger.Component
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.ActiveSessionObservableStore
import im.vector.riotx.EmojiCompatFontProvider
import im.vector.riotx.EmojiCompatWrapper
import im.vector.riotx.VectorApplication
@ -42,6 +43,7 @@ import im.vector.riotx.features.rageshake.VectorFileLogger
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotx.features.session.SessionListener
import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.share.ShareRoomListObservableStore
import im.vector.riotx.features.ui.UiStateRepository
import javax.inject.Singleton
@ -85,8 +87,12 @@ interface VectorComponent {
fun homeRoomListObservableStore(): HomeRoomListObservableStore
fun shareRoomListObservableStore(): ShareRoomListObservableStore
fun selectedGroupStore(): SelectedGroupStore
fun activeSessionObservableStore(): ActiveSessionObservableStore
fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler
fun incomingKeyRequestHandler(): KeyRequestHandler

View file

@ -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.riotx.features.home.room.list
import dagger.Binds
import dagger.Module
@Module
abstract class RoomListModule {
@Binds
abstract fun providesRoomListViewModelFactory(roomListViewModelFactory: RoomListViewModelFactory): RoomListViewModel.Factory
}

View file

@ -21,8 +21,6 @@ import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.Membership
@ -31,18 +29,18 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTag
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.utils.LiveEvent
import im.vector.riotx.features.home.HomeRoomListObservableStore
import im.vector.riotx.core.utils.RxStore
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
import javax.inject.Inject
class RoomListViewModel @AssistedInject constructor(@Assisted initialState: RoomListViewState,
private val session: Session,
private val homeRoomListObservableSource: HomeRoomListObservableStore,
private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
private val chronologicalRoomComparator: ChronologicalRoomComparator)
class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
private val session: Session,
private val roomSummariesStore: RxStore<List<RoomSummary>>,
private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
private val chronologicalRoomComparator: ChronologicalRoomComparator)
: VectorViewModel<RoomListViewState>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: RoomListViewState): RoomListViewModel
}
@ -101,7 +99,7 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
}
private fun observeRoomSummaries() {
homeRoomListObservableSource
roomSummariesStore
.observe()
.observeOn(Schedulers.computation())
.map {
@ -111,7 +109,7 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
copy(asyncRooms = asyncRooms)
}
homeRoomListObservableSource
roomSummariesStore
.observe()
.observeOn(Schedulers.computation())
.map { buildRoomSummaries(it) }

View file

@ -0,0 +1,39 @@
/*
* 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.room.list
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.features.home.HomeRoomListObservableStore
import im.vector.riotx.features.share.ShareRoomListObservableStore
import javax.inject.Inject
import javax.inject.Provider
class RoomListViewModelFactory @Inject constructor(private val session: Provider<Session>,
private val homeRoomListObservableStore: Provider<HomeRoomListObservableStore>,
private val shareRoomListObservableStore: Provider<ShareRoomListObservableStore>,
private val alphabeticalRoomComparator: Provider<AlphabeticalRoomComparator>,
private val chronologicalRoomComparator: Provider<ChronologicalRoomComparator>) : RoomListViewModel.Factory {
override fun create(initialState: RoomListViewState): RoomListViewModel {
return RoomListViewModel(
initialState,
session.get(),
if (initialState.displayMode == RoomListFragment.DisplayMode.SHARE) shareRoomListObservableStore.get() else homeRoomListObservableStore.get(),
alphabeticalRoomComparator.get(),
chronologicalRoomComparator.get())
}
}

View file

@ -20,6 +20,8 @@ import androidx.annotation.StringRes
import com.airbnb.epoxy.EpoxyController
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.noResultItem
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.home.room.filtered.FilteredRoomFooterItem
import im.vector.riotx.features.home.room.filtered.filteredRoomFooterItem
@ -47,24 +49,28 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
override fun buildModels() {
val nonNullViewState = viewState ?: return
if (nonNullViewState.displayMode == RoomListFragment.DisplayMode.FILTERED) {
buildFilteredRooms(nonNullViewState)
} else {
val roomSummaries = nonNullViewState.asyncFilteredRooms()
roomSummaries?.forEach { (category, summaries) ->
if (summaries.isEmpty()) {
return@forEach
} else {
val isExpanded = nonNullViewState.isCategoryExpanded(category)
buildRoomCategory(nonNullViewState, summaries, category.titleRes, nonNullViewState.isCategoryExpanded(category)) {
listener?.onToggleRoomCategory(category)
}
if (isExpanded) {
buildRoomModels(summaries,
nonNullViewState.joiningRoomsIds,
nonNullViewState.joiningErrorRoomsIds,
nonNullViewState.rejectingRoomsIds,
nonNullViewState.rejectingErrorRoomsIds)
when (nonNullViewState.displayMode) {
RoomListFragment.DisplayMode.FILTERED,
RoomListFragment.DisplayMode.SHARE -> {
buildFilteredRooms(nonNullViewState)
}
else -> {
val roomSummaries = nonNullViewState.asyncFilteredRooms()
roomSummaries?.forEach { (category, summaries) ->
if (summaries.isEmpty()) {
return@forEach
} else {
val isExpanded = nonNullViewState.isCategoryExpanded(category)
buildRoomCategory(nonNullViewState, summaries, category.titleRes, nonNullViewState.isCategoryExpanded(category)) {
listener?.onToggleRoomCategory(category)
}
if (isExpanded) {
buildRoomModels(summaries,
nonNullViewState.joiningRoomsIds,
nonNullViewState.joiningErrorRoomsIds,
nonNullViewState.rejectingRoomsIds,
nonNullViewState.rejectingErrorRoomsIds)
}
}
}
}
@ -80,12 +86,15 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
.filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) }
buildRoomModels(filteredSummaries,
viewState.joiningRoomsIds,
viewState.joiningErrorRoomsIds,
viewState.rejectingRoomsIds,
viewState.rejectingErrorRoomsIds)
viewState.joiningRoomsIds,
viewState.joiningErrorRoomsIds,
viewState.rejectingRoomsIds,
viewState.rejectingErrorRoomsIds)
addFilterFooter(viewState)
when {
viewState.displayMode == RoomListFragment.DisplayMode.FILTERED -> addFilterFooter(viewState)
filteredSummaries.isEmpty() -> addEmptyFooter()
}
}
private fun addFilterFooter(viewState: RoomListViewState) {
@ -96,6 +105,13 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
}
}
private fun addEmptyFooter() {
noResultItem {
id("no_result")
text(stringProvider.getString(R.string.no_result_placeholder))
}
}
private fun buildRoomCategory(viewState: RoomListViewState,
summaries: List<RoomSummary>,
@StringRes titleRes: Int,

View file

@ -20,6 +20,8 @@ import android.content.ClipDescription
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.widget.SearchView
import com.airbnb.mvrx.viewModel
import com.kbeanie.multipicker.utils.IntentUtils
import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.riotx.R
@ -39,8 +41,10 @@ class IncomingShareActivity :
VectorBaseActivity(), AttachmentsHelper.Callback {
@Inject lateinit var sessionHolder: ActiveSessionHolder
private lateinit var roomListFragment: RoomListFragment
@Inject lateinit var incomingShareViewModelFactory: IncomingShareViewModel.Factory
private var roomListFragment: RoomListFragment? = null
private lateinit var attachmentsHelper: AttachmentsHelper
private val incomingShareViewModel: IncomingShareViewModel by viewModel()
override fun getLayoutRes(): Int {
return R.layout.activity_incoming_share
@ -77,12 +81,23 @@ class IncomingShareActivity :
} else {
cannotManageShare()
}
incomingShareSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return true
}
override fun onQueryTextChange(newText: String): Boolean {
roomListFragment?.filterRoomsWith(newText)
return true
}
})
}
override fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) {
val roomListParams = RoomListParams(RoomListFragment.DisplayMode.SHARE, sharedData = SharedData.Attachments(attachments))
roomListFragment = RoomListFragment.newInstance(roomListParams)
replaceFragment(roomListFragment, R.id.shareRoomListFragmentContainer)
.also { replaceFragment(it, R.id.shareRoomListFragmentContainer) }
}
override fun onAttachmentsProcessFailed() {
@ -102,7 +117,7 @@ class IncomingShareActivity :
} else {
val roomListParams = RoomListParams(RoomListFragment.DisplayMode.SHARE, sharedData = SharedData.Text(sharedText))
roomListFragment = RoomListFragment.newInstance(roomListParams)
replaceFragment(roomListFragment, R.id.shareRoomListFragmentContainer)
.also { replaceFragment(it, R.id.shareRoomListFragmentContainer) }
true
}
}

View file

@ -0,0 +1,73 @@
/*
* 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.share
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.rx.rx
import im.vector.riotx.ActiveSessionObservableStore
import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit
data class IncomingShareState(private val dummy: Boolean = false) : MvRxState
/**
* View model used to observe the room list and post update to the ShareRoomListObservableStore
*/
class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: IncomingShareState,
private val sessionObservableStore: ActiveSessionObservableStore,
private val shareRoomListObservableStore: ShareRoomListObservableStore)
: VectorViewModel<IncomingShareState>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: IncomingShareState): IncomingShareViewModel
}
companion object : MvRxViewModelFactory<IncomingShareViewModel, IncomingShareState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: IncomingShareState): IncomingShareViewModel? {
val activity: IncomingShareActivity = (viewModelContext as ActivityViewModelContext).activity()
return activity.incomingShareViewModelFactory.create(state)
}
}
init {
observeRoomSummaries()
}
private fun observeRoomSummaries() {
sessionObservableStore.observe()
.observeOn(AndroidSchedulers.mainThread())
.switchMap {
it.orNull()?.rx()?.liveRoomSummaries()
?: Observable.just(emptyList())
}
.throttleLast(300, TimeUnit.MILLISECONDS)
.subscribe {
shareRoomListObservableStore.post(it)
}
.disposeOnClear()
}
}

View file

@ -0,0 +1,25 @@
/*
* 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.share
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotx.core.utils.RxStore
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ShareRoomListObservableStore @Inject constructor() : RxStore<List<RoomSummary>>()