From 53b1b89c4758b273e517dd2e7b467b5f92949fdf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Sep 2019 17:13:52 +0200 Subject: [PATCH 1/5] after login, the icon in the top left is a green 'A' for (all communities) rather than my avatar (#267) --- CHANGES.md | 1 + .../im/vector/matrix/rx/LiveDataObservable.kt | 1 - .../java/im/vector/matrix/rx/RxSession.kt | 5 +++ .../android/api/session/user/UserService.kt | 5 ++- .../matrix/android/api/util/Optional.kt | 42 +++++++++++++++++++ .../session/user/DefaultUserService.kt | 5 ++- .../riotx/features/home/HomeDrawerFragment.kt | 3 +- .../features/home/group/GroupListViewModel.kt | 34 +++++++++------ 8 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt diff --git a/CHANGES.md b/CHANGES.md index dc4c743bc4..fd141bcdfb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,7 @@ Other changes: Bugfix: - Fix issue on upload error in loop (#587) + - after login, the icon in the top left is a green 'A' for (all communities) rather than my avatar (#267) Translations: - diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/LiveDataObservable.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/LiveDataObservable.kt index a1943bbe1c..9eaeff762e 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/LiveDataObservable.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/LiveDataObservable.kt @@ -20,7 +20,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import io.reactivex.Observable import io.reactivex.android.MainThreadDisposable -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers private class LiveDataObservable( diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt index f3fb06a45a..d8c254844b 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.user.model.User +import im.vector.matrix.android.api.util.Optional import io.reactivex.Observable import io.reactivex.Single @@ -45,6 +46,10 @@ class RxSession(private val session: Session) { return session.livePushers().asObservable() } + fun liveUser(userId: String): Observable> { + return session.liveUser(userId).asObservable() + } + fun liveUsers(): Observable> { return session.liveUsers().asObservable() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/UserService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/UserService.kt index d3c58edd94..97b02fafea 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/UserService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/UserService.kt @@ -21,6 +21,7 @@ import androidx.paging.PagedList import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.Optional /** * This interface defines methods to get users. It's implemented at the session level. @@ -47,9 +48,9 @@ interface UserService { /** * Observe a live user from a userId * @param userId the userId to look for. - * @return a Livedata of user with userId + * @return a LiveData of user with userId */ - fun liveUser(userId: String): LiveData + fun liveUser(userId: String): LiveData> /** * Observe a live list of users sorted alphabetically diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt new file mode 100644 index 0000000000..19fbe2cc88 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt @@ -0,0 +1,42 @@ +/* + + * 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.api.util + +data class Optional constructor(private val value: T?) { + + fun get(): T { + return value!! + } + + fun getOrNull(): T? { + return value + } + + fun getOrElse(fn: () -> T): T { + return value ?: fn() + } + + companion object { + fun from(value: T?): Optional { + return Optional(value) + } + } + +} + +fun T?.toOptional() = Optional(this) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt index 2925997347..40e79d7735 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt @@ -26,6 +26,8 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.Optional +import im.vector.matrix.android.api.util.toOptional import im.vector.matrix.android.internal.database.RealmLiveData import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.UserEntity @@ -66,7 +68,7 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona return userEntity.asDomain() } - override fun liveUser(userId: String): LiveData { + override fun liveUser(userId: String): LiveData> { val liveRealmData = RealmLiveData(monarchy.realmConfiguration) { realm -> UserEntity.where(realm, userId) } @@ -74,6 +76,7 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona results .map { it.asDomain() } .firstOrNull() + .toOptional() } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt index ad39839321..e5f0c5b2d3 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt @@ -51,7 +51,8 @@ class HomeDrawerFragment : VectorBaseFragment() { val groupListFragment = GroupListFragment.newInstance() replaceChildFragment(groupListFragment, R.id.homeDrawerGroupListContainer) } - session.liveUser(session.myUserId).observeK(this) { user -> + session.liveUser(session.myUserId).observeK(this) { optionalUser -> + val user = optionalUser?.getOrNull() if (user != null) { avatarRenderer.render(user.avatarUrl, user.userId, user.displayName, homeDrawerHeaderAvatarView) homeDrawerUsernameView.text = user.displayName diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt index 0be22e411e..3f2a2e2958 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt @@ -33,6 +33,8 @@ import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.LiveEvent +import io.reactivex.Observable +import io.reactivex.functions.BiFunction const val ALL_COMMUNITIES_GROUP_ID = "ALL_COMMUNITIES_GROUP_ID" @@ -91,20 +93,26 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro } private fun observeGroupSummaries() { - session - .rx() - .liveGroupSummaries() - // Keep only joined groups. Group invitations will be managed later - .map { it.filter { groupSummary -> groupSummary.membership == Membership.JOIN } } - .map { - val myUser = session.getUser(session.myUserId) - val allCommunityGroup = GroupSummary( - groupId = ALL_COMMUNITIES_GROUP_ID, - membership = Membership.JOIN, - displayName = stringProvider.getString(R.string.group_all_communities), - avatarUrl = myUser?.avatarUrl ?: "") - listOf(allCommunityGroup) + it + Observable.combineLatest, List>( + session + .rx() + .liveUser(session.myUserId) + .map { optionalUser -> + GroupSummary( + groupId = ALL_COMMUNITIES_GROUP_ID, + membership = Membership.JOIN, + displayName = stringProvider.getString(R.string.group_all_communities), + avatarUrl = optionalUser.getOrNull()?.avatarUrl ?: "") + }, + session + .rx() + .liveGroupSummaries() + // Keep only joined groups. Group invitations will be managed later + .map { it.filter { groupSummary -> groupSummary.membership == Membership.JOIN } }, + BiFunction { allCommunityGroup, communityGroups -> + listOf(allCommunityGroup) + communityGroups } + ) .execute { async -> val newSelectedGroup = selectedGroup ?: async()?.firstOrNull() copy(asyncGroups = async, selectedGroup = newSelectedGroup) From 84542326f433ba741e9e08392d85770c62219bfc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Sep 2019 18:06:24 +0200 Subject: [PATCH 2/5] HomeDetailFragment observe the selectedGroupStore instead of passing argument --- .../riotx/features/home/HomeDetailFragment.kt | 39 ++++++++----------- .../features/home/HomeDetailViewModel.kt | 14 +++++++ .../features/home/HomeDetailViewState.kt | 3 ++ .../riotx/features/home/HomeNavigator.kt | 3 +- .../features/home/group/GroupListViewModel.kt | 4 +- 5 files changed, 37 insertions(+), 26 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt index 112fae1b24..f6e8cc199b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt @@ -17,18 +17,17 @@ package im.vector.riotx.features.home import android.os.Bundle -import android.os.Parcelable import android.view.LayoutInflater import androidx.core.view.forEachIndexed import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders -import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.bottomnavigation.BottomNavigationItemView import com.google.android.material.bottomnavigation.BottomNavigationMenuView import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState +import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.platform.ToolbarConfigurable @@ -38,26 +37,16 @@ import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.home.room.list.RoomListParams import im.vector.riotx.features.home.room.list.UnreadCounterBadgeView import im.vector.riotx.features.workers.signout.SignOutViewModel -import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_home_detail.* import javax.inject.Inject -@Parcelize -data class HomeDetailParams( - val groupId: String, - val groupName: String, - val groupAvatar: String -) : Parcelable - - private const val INDEX_CATCHUP = 0 private const val INDEX_PEOPLE = 1 private const val INDEX_ROOMS = 2 class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate { - private val params: HomeDetailParams by args() private val unreadCounterBadgeViews = arrayListOf() private val viewModel: HomeDetailViewModel by fragmentViewModel() @@ -84,11 +73,25 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate { setupToolbar() setupKeysBackupBanner() + viewModel.selectSubscribe(this, HomeDetailViewState::groupSummary) { groupSummary -> + onGroupChange(groupSummary.orNull()) + } viewModel.selectSubscribe(this, HomeDetailViewState::displayMode) { displayMode -> switchDisplayMode(displayMode) } } + private fun onGroupChange(groupSummary: GroupSummary?) { + groupSummary?.let { + avatarRenderer.render( + it.avatarUrl, + it.groupId, + it.displayName, + groupToolbarAvatarImageView + ) + } + } + private fun setupKeysBackupBanner() { // Keys backup banner // Use the SignOutViewModel, it observe the keys backup state and this is what we need here @@ -130,12 +133,6 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate { parentActivity.configure(groupToolbar) } groupToolbar.title = "" - avatarRenderer.render( - params.groupAvatar, - params.groupId, - params.groupName, - groupToolbarAvatarImageView - ) groupToolbarAvatarImageView.setOnClickListener { navigationViewModel.goTo(HomeActivity.Navigation.OpenDrawer) } @@ -207,10 +204,8 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate { companion object { - fun newInstance(args: HomeDetailParams): HomeDetailFragment { - return HomeDetailFragment().apply { - setArguments(args) - } + fun newInstance(): HomeDetailFragment { + return HomeDetailFragment() } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt index 688d2b6b7b..a825b64979 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt @@ -25,6 +25,7 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.rx.rx import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.features.home.group.SelectedGroupStore import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.ui.UiStateRepository import io.reactivex.schedulers.Schedulers @@ -36,6 +37,7 @@ import io.reactivex.schedulers.Schedulers class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState, private val session: Session, private val uiStateRepository: UiStateRepository, + private val selectedGroupStore: SelectedGroupStore, private val homeRoomListStore: HomeRoomListObservableStore) : VectorViewModel(initialState) { @@ -62,6 +64,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho init { observeSyncState() + observeSelectedGroupStore() observeRoomSummaries() } @@ -88,6 +91,17 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho .disposeOnClear() } + private fun observeSelectedGroupStore() { + selectedGroupStore + .observe() + .subscribe { + setState { + copy(groupSummary = it) + } + } + .disposeOnClear() + } + private fun observeRoomSummaries() { homeRoomListStore .observe() diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewState.kt index a8f89cc566..cb2c07835d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewState.kt @@ -16,11 +16,14 @@ package im.vector.riotx.features.home +import arrow.core.Option import com.airbnb.mvrx.MvRxState +import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.sync.SyncState import im.vector.riotx.features.home.room.list.RoomListFragment data class HomeDetailViewState( + val groupSummary: Option = Option.empty(), val displayMode: RoomListFragment.DisplayMode = RoomListFragment.DisplayMode.HOME, val notificationCountCatchup: Int = 0, val notificationHighlightCatchup: Boolean = false, diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeNavigator.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeNavigator.kt index 680d7bbc33..ca1d12d95f 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeNavigator.kt @@ -36,8 +36,7 @@ class HomeNavigator @Inject constructor() { activity?.let { it.drawerLayout?.closeDrawer(GravityCompat.START) - val args = HomeDetailParams(groupSummary.groupId, groupSummary.displayName, groupSummary.avatarUrl) - val homeDetailFragment = HomeDetailFragment.newInstance(args) + val homeDetailFragment = HomeDetailFragment.newInstance() it.replaceFragment(homeDetailFragment, R.id.homeDetailFragmentContainer) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt index 3f2a2e2958..2ca8c414fb 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt @@ -39,7 +39,7 @@ import io.reactivex.functions.BiFunction const val ALL_COMMUNITIES_GROUP_ID = "ALL_COMMUNITIES_GROUP_ID" class GroupListViewModel @AssistedInject constructor(@Assisted initialState: GroupListViewState, - private val selectedGroupHolder: SelectedGroupStore, + private val selectedGroupStore: SelectedGroupStore, private val session: Session, private val stringProvider: StringProvider ) : VectorViewModel(initialState) { @@ -73,7 +73,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro if (it != null) { _openGroupLiveData.postLiveEvent(it) val optionGroup = Option.fromNullable(it) - selectedGroupHolder.post(optionGroup) + selectedGroupStore.post(optionGroup) } } } From 2d95fe921df8adcb5d593949ee05f19bbd663f86 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Sep 2019 18:42:27 +0200 Subject: [PATCH 3/5] after login, the icon in the top left is a green 'A' for (all communities) rather than my avatar (#267) - part2 (Toolbar) --- .../features/home/HomeDetailViewModel.kt | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt index a825b64979..b92c821671 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt @@ -16,18 +16,27 @@ package im.vector.riotx.features.home +import arrow.core.Option +import arrow.core.toOption 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.session.Session +import im.vector.matrix.android.api.session.group.model.GroupSummary +import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.rx.rx +import im.vector.riotx.R import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.features.home.group.ALL_COMMUNITIES_GROUP_ID import im.vector.riotx.features.home.group.SelectedGroupStore import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.ui.UiStateRepository +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers /** @@ -38,7 +47,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho private val session: Session, private val uiStateRepository: UiStateRepository, private val selectedGroupStore: SelectedGroupStore, - private val homeRoomListStore: HomeRoomListObservableStore) + private val homeRoomListStore: HomeRoomListObservableStore, + private val stringProvider: StringProvider) : VectorViewModel(initialState) { @AssistedInject.Factory @@ -94,6 +104,37 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho private fun observeSelectedGroupStore() { selectedGroupStore .observe() + // TODO I do not like that, but it crashes with + // Thread: RxComputationThreadPool-2, Exception: java.lang.IllegalStateException: Cannot invoke observeForever on a background thread + .observeOn(AndroidSchedulers.mainThread()) + .flatMap { optionGroupSummary -> + val group = optionGroupSummary.orNull() + when { + group == null -> + Observable.just(Option.empty()) + group.groupId == ALL_COMMUNITIES_GROUP_ID -> + session + .rx() + .liveUser(session.myUserId) + .map { optionalUser -> + GroupSummary( + groupId = ALL_COMMUNITIES_GROUP_ID, + membership = Membership.JOIN, + displayName = stringProvider.getString(R.string.group_all_communities), + avatarUrl = optionalUser.getOrNull()?.avatarUrl ?: "") + .toOption() + } + else -> + session + .rx() + .liveGroupSummaries() + .map { it.filter { groupSummary -> groupSummary.groupId == group.groupId } } + .map { + it.firstOrNull().toOption() + } + } + } + .observeOn(Schedulers.computation()) .subscribe { setState { copy(groupSummary = it) From de30e7c1c600ca4b699582206f5d3c7321b0b261 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Sep 2019 19:00:38 +0200 Subject: [PATCH 4/5] Code cleanup --- .../features/home/HomeDetailViewModel.kt | 66 +++++++++---------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt index b92c821671..5a1364e9a4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt @@ -16,7 +16,8 @@ package im.vector.riotx.features.home -import arrow.core.Option +import arrow.core.None +import arrow.core.firstOrNone import arrow.core.toOption import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory @@ -111,7 +112,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho val group = optionGroupSummary.orNull() when { group == null -> - Observable.just(Option.empty()) + Observable.just(None) group.groupId == ALL_COMMUNITIES_GROUP_ID -> session .rx() @@ -122,16 +123,14 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho membership = Membership.JOIN, displayName = stringProvider.getString(R.string.group_all_communities), avatarUrl = optionalUser.getOrNull()?.avatarUrl ?: "") - .toOption() } + .map { it.toOption() } else -> session .rx() .liveGroupSummaries() .map { it.filter { groupSummary -> groupSummary.groupId == group.groupId } } - .map { - it.firstOrNull().toOption() - } + .map { it.firstOrNone() } } } .observeOn(Schedulers.computation()) @@ -147,38 +146,33 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho homeRoomListStore .observe() .observeOn(Schedulers.computation()) - .subscribe { list -> - list.let { summaries -> - val peopleNotifications = summaries - .filter { it.isDirect } - .map { it.notificationCount } - .takeIf { it.isNotEmpty() } - ?.sumBy { i -> i } - ?: 0 - val peopleHasHighlight = summaries - .filter { it.isDirect } - .any { it.highlightCount > 0 } + .map { it.asSequence() } + .subscribe { summaries -> + val peopleNotifications = summaries + .filter { it.isDirect } + .map { it.notificationCount } + .sumBy { i -> i } + val peopleHasHighlight = summaries + .filter { it.isDirect } + .any { it.highlightCount > 0 } - val roomsNotifications = summaries - .filter { !it.isDirect } - .map { it.notificationCount } - .takeIf { it.isNotEmpty() } - ?.sumBy { i -> i } - ?: 0 - val roomsHasHighlight = summaries - .filter { !it.isDirect } - .any { it.highlightCount > 0 } + val roomsNotifications = summaries + .filter { !it.isDirect } + .map { it.notificationCount } + .sumBy { i -> i } + val roomsHasHighlight = summaries + .filter { !it.isDirect } + .any { it.highlightCount > 0 } - setState { - copy( - notificationCountCatchup = peopleNotifications + roomsNotifications, - notificationHighlightCatchup = peopleHasHighlight || roomsHasHighlight, - notificationCountPeople = peopleNotifications, - notificationHighlightPeople = peopleHasHighlight, - notificationCountRooms = roomsNotifications, - notificationHighlightRooms = roomsHasHighlight - ) - } + setState { + copy( + notificationCountCatchup = peopleNotifications + roomsNotifications, + notificationHighlightCatchup = peopleHasHighlight || roomsHasHighlight, + notificationCountPeople = peopleNotifications, + notificationHighlightPeople = peopleHasHighlight, + notificationCountRooms = roomsNotifications, + notificationHighlightRooms = roomsHasHighlight + ) } } .disposeOnClear() From 0814f53fed0f4c867d90413d62b1851e7bb9ce15 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 30 Sep 2019 19:36:19 +0200 Subject: [PATCH 5/5] Group avatar: clean and optimize a bit. --- .../java/im/vector/matrix/rx/RxSession.kt | 2 +- .../im/vector/riotx/core/utils/RxStore.kt | 26 +++++++++---- .../features/home/HomeActivityViewModel.kt | 3 +- .../riotx/features/home/HomeDetailFragment.kt | 2 + .../features/home/HomeDetailViewModel.kt | 38 ------------------- .../features/home/group/GroupListViewModel.kt | 13 ++++++- 6 files changed, 35 insertions(+), 49 deletions(-) diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt index d8c254844b..0d0cb2f461 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt @@ -47,7 +47,7 @@ class RxSession(private val session: Session) { } fun liveUser(userId: String): Observable> { - return session.liveUser(userId).asObservable() + return session.liveUser(userId).asObservable().distinctUntilChanged() } fun liveUsers(): Observable> { diff --git a/vector/src/main/java/im/vector/riotx/core/utils/RxStore.kt b/vector/src/main/java/im/vector/riotx/core/utils/RxStore.kt index 89780b2463..94f2a9c912 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/RxStore.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/RxStore.kt @@ -20,19 +20,31 @@ import com.jakewharton.rxrelay2.BehaviorRelay import io.reactivex.Observable import io.reactivex.schedulers.Schedulers -open class RxStore(defaultValue: T? = null) { +open class RxStore(private val defaultValue: T? = null) { - private val storeSubject: BehaviorRelay = if (defaultValue == null) { - BehaviorRelay.create() - } else { - BehaviorRelay.createDefault(defaultValue) + var storeRelay = createRelay() + + fun clear() { + storeRelay = createRelay() + } + + fun get(): T? { + return storeRelay.value } fun observe(): Observable { - return storeSubject.hide().observeOn(Schedulers.computation()) + return storeRelay.hide().observeOn(Schedulers.computation()) } fun post(value: T) { - storeSubject.accept(value) + storeRelay.accept(value) + } + + private fun createRelay(): BehaviorRelay { + return if (defaultValue == null) { + BehaviorRelay.create() + } else { + BehaviorRelay.createDefault(defaultValue) + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeActivityViewModel.kt index f8c1eca19e..971f3d602c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeActivityViewModel.kt @@ -98,7 +98,8 @@ class HomeActivityViewModel @AssistedInject constructor(@Assisted initialState: override fun onCleared() { super.onCleared() - + selectedGroupStore.clear() + homeRoomListStore.clear() session.removeListener(this) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt index f6e8cc199b..955973011c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt @@ -38,6 +38,7 @@ import im.vector.riotx.features.home.room.list.RoomListParams import im.vector.riotx.features.home.room.list.UnreadCounterBadgeView import im.vector.riotx.features.workers.signout.SignOutViewModel import kotlinx.android.synthetic.main.fragment_home_detail.* +import timber.log.Timber import javax.inject.Inject @@ -196,6 +197,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate { } override fun invalidate() = withState(viewModel) { + Timber.v(it.toString()) unreadCounterBadgeViews[INDEX_CATCHUP].render(UnreadCounterBadgeView.State(it.notificationCountCatchup, it.notificationHighlightCatchup)) unreadCounterBadgeViews[INDEX_PEOPLE].render(UnreadCounterBadgeView.State(it.notificationCountPeople, it.notificationHighlightPeople)) unreadCounterBadgeViews[INDEX_ROOMS].render(UnreadCounterBadgeView.State(it.notificationCountRooms, it.notificationHighlightRooms)) diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt index 5a1364e9a4..c8e3231362 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt @@ -16,28 +16,19 @@ package im.vector.riotx.features.home -import arrow.core.None -import arrow.core.firstOrNone -import arrow.core.toOption 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.session.Session -import im.vector.matrix.android.api.session.group.model.GroupSummary -import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.rx.rx -import im.vector.riotx.R import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.resources.StringProvider -import im.vector.riotx.features.home.group.ALL_COMMUNITIES_GROUP_ID import im.vector.riotx.features.home.group.SelectedGroupStore import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.ui.UiStateRepository -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers /** @@ -105,35 +96,6 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho private fun observeSelectedGroupStore() { selectedGroupStore .observe() - // TODO I do not like that, but it crashes with - // Thread: RxComputationThreadPool-2, Exception: java.lang.IllegalStateException: Cannot invoke observeForever on a background thread - .observeOn(AndroidSchedulers.mainThread()) - .flatMap { optionGroupSummary -> - val group = optionGroupSummary.orNull() - when { - group == null -> - Observable.just(None) - group.groupId == ALL_COMMUNITIES_GROUP_ID -> - session - .rx() - .liveUser(session.myUserId) - .map { optionalUser -> - GroupSummary( - groupId = ALL_COMMUNITIES_GROUP_ID, - membership = Membership.JOIN, - displayName = stringProvider.getString(R.string.group_all_communities), - avatarUrl = optionalUser.getOrNull()?.avatarUrl ?: "") - } - .map { it.toOption() } - else -> - session - .rx() - .liveGroupSummaries() - .map { it.filter { groupSummary -> groupSummary.groupId == group.groupId } } - .map { it.firstOrNone() } - } - } - .observeOn(Schedulers.computation()) .subscribe { setState { copy(groupSummary = it) diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt index 2ca8c414fb..c7278ff81d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt @@ -71,7 +71,11 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro private fun observeSelectionState() { selectSubscribe(GroupListViewState::selectedGroup) { if (it != null) { - _openGroupLiveData.postLiveEvent(it) + val selectedGroup = selectedGroupStore.get()?.orNull() + // We only wan to open group if the updated selectedGroup is a different one. + if (selectedGroup?.groupId != it.groupId) { + _openGroupLiveData.postLiveEvent(it) + } val optionGroup = Option.fromNullable(it) selectedGroupStore.post(optionGroup) } @@ -114,7 +118,12 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro } ) .execute { async -> - val newSelectedGroup = selectedGroup ?: async()?.firstOrNull() + val currentSelectedGroupId = selectedGroup?.groupId + val newSelectedGroup = if (currentSelectedGroupId != null) { + async()?.find { it.groupId == currentSelectedGroupId } + } else { + async()?.firstOrNull() + } copy(asyncGroups = async, selectedGroup = newSelectedGroup) } }