diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index 1f090c89ab1..0df41d106de 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -22,17 +22,25 @@ import androidx.lifecycle.OnLifecycleEvent import arrow.core.Option import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.BehaviorDataSource -import im.vector.app.features.spaces.ALL_COMMUNITIES_GROUP_ID import im.vector.app.features.ui.UiStateRepository import io.reactivex.disposables.CompositeDisposable import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject import javax.inject.Singleton +sealed class RoomGroupingMethod { + data class ByLegacyGroup(val groupSummary: GroupSummary?) : RoomGroupingMethod() + data class BySpace(val spaceSummary: RoomSummary?) : RoomGroupingMethod() +} + +fun RoomGroupingMethod.space() = (this as? RoomGroupingMethod.BySpace)?.spaceSummary +fun RoomGroupingMethod.group() = (this as? RoomGroupingMethod.ByLegacyGroup)?.groupSummary + /** * This class handles the global app state. * It requires to be added to ProcessLifecycleOwner.get().lifecycle @@ -47,43 +55,65 @@ class AppStateHandler @Inject constructor( private val compositeDisposable = CompositeDisposable() - private val selectedSpaceDataSource = BehaviorDataSource>(Option.empty()) + private val selectedSpaceDataSource = BehaviorDataSource>(Option.empty()) + + val selectedRoomGroupingObservable = selectedSpaceDataSource.observe() - val selectedSpaceObservable = selectedSpaceDataSource.observe() + fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? = selectedSpaceDataSource.currentValue?.orNull() - fun setCurrentSpace(space: RoomSummary?) { - if (space == selectedSpaceDataSource.currentValue?.orNull()) return - selectedSpaceDataSource.post(space?.let { Option.just(it) } ?: Option.empty()) - if (space != null && space.roomId != ALL_COMMUNITIES_GROUP_ID) { + fun setCurrentSpace(spaceId: String?, session: Session? = null) { + val uSession = session ?: activeSessionHolder.getSafeActiveSession() + if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.BySpace + && spaceId == selectedSpaceDataSource.currentValue?.orNull()?.space()?.roomId) return + val spaceSum = spaceId?.let { uSession?.getRoomSummary(spaceId) } + selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.BySpace(spaceSum))) + if (spaceId != null) { GlobalScope.launch { tryOrNull { - activeSessionHolder.getSafeActiveSession()?.getRoom(space.roomId)?.loadRoomMembersIfNeeded() + uSession?.getRoom(spaceId)?.loadRoomMembersIfNeeded() } } } } - init { - // restore current space from ui state - sessionDataSource.currentValue?.orNull()?.let { session -> - uiStateRepository.getSelectedSpace(session.sessionId)?.let { selectedSpaceId -> - session.getRoomSummary(selectedSpaceId)?.let { - setCurrentSpace(it) + fun setCurrentGroup(groupId: String?, session: Session? = null) { + val uSession = session ?: activeSessionHolder.getSafeActiveSession() + if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.ByLegacyGroup + && groupId == selectedSpaceDataSource.currentValue?.orNull()?.group()?.groupId) return + val activeGroup = groupId?.let { uSession?.getGroupSummary(groupId) } + selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.ByLegacyGroup(activeGroup))) + if (groupId != null) { + GlobalScope.launch { + tryOrNull { + uSession?.getGroup(groupId)?.fetchGroupData() } } } } + init { + sessionDataSource.observe() + .distinctUntilChanged() + .subscribe { + // sessionDataSource could already return a session while acitveSession holder still returns null + it.orNull()?.let { session -> + if (uiStateRepository.isGroupingMethodSpace(session.sessionId)) { + setCurrentSpace(uiStateRepository.getSelectedSpace(session.sessionId), session) + } else { + setCurrentGroup(uiStateRepository.getSelectedGroup(session.sessionId), session) + } + } + }.also { + compositeDisposable.add(it) + } + } + fun safeActiveSpaceId(): String? { - return selectedSpaceDataSource.currentValue?.orNull()?.roomId?.takeIf { - MatrixPatterns.isRoomId(it) - } + return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId } - fun safeActiveSpace(): RoomSummary? { - return selectedSpaceDataSource.currentValue?.orNull()?.takeIf { - MatrixPatterns.isRoomId(it.roomId) - } + fun safeActiveGroupId(): String? { + return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.ByLegacyGroup)?.groupSummary?.groupId } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @@ -93,5 +123,16 @@ class AppStateHandler @Inject constructor( @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun entersBackground() { compositeDisposable.clear() + val session = activeSessionHolder.getSafeActiveSession() ?: return + when (val currentMethod = selectedSpaceDataSource.currentValue?.orNull() ?: RoomGroupingMethod.BySpace(null)) { + is RoomGroupingMethod.BySpace -> { + uiStateRepository.storeGroupingMethod(true, session.sessionId) + uiStateRepository.storeSelectedSpace(currentMethod.spaceSummary?.roomId, session.sessionId) + } + is RoomGroupingMethod.ByLegacyGroup -> { + uiStateRepository.storeGroupingMethod(false, session.sessionId) + uiStateRepository.storeSelectedGroup(currentMethod.groupSummary?.groupId, session.sessionId) + } + } } } diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 3329c9d3722..3d15bdd1230 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -51,7 +51,6 @@ import im.vector.app.features.devtools.RoomDevToolSendFormFragment import im.vector.app.features.devtools.RoomDevToolStateEventListFragment import im.vector.app.features.discovery.DiscoverySettingsFragment import im.vector.app.features.discovery.change.SetIdentityServerFragment -import im.vector.app.features.grouplist.GroupListFragment import im.vector.app.features.home.HomeDetailFragment import im.vector.app.features.home.HomeDrawerFragment import im.vector.app.features.home.LoadingFragment @@ -150,11 +149,6 @@ interface FragmentModule { @FragmentKey(LocalePickerFragment::class) fun bindLocalePickerFragment(fragment: LocalePickerFragment): Fragment - @Binds - @IntoMap - @FragmentKey(GroupListFragment::class) - fun bindGroupListFragment(fragment: GroupListFragment): Fragment - @Binds @IntoMap @FragmentKey(SpaceListFragment::class) diff --git a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt index c8d5f6dd202..e5a47e872c9 100644 --- a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt @@ -35,7 +35,6 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.crypto.keysrequest.KeyRequestHandler import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler -import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.CurrentSpaceSuggestedRoomListDataSource import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore @@ -115,8 +114,6 @@ interface VectorComponent { fun errorFormatter(): ErrorFormatter - fun selectedGroupStore(): SelectedGroupDataSource - fun appStateHandler(): AppStateHandler fun currentSpaceSuggestedRoomListDataSource(): CurrentSpaceSuggestedRoomListDataSource diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListAction.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListAction.kt deleted file mode 100644 index 4d974b8ce80..00000000000 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListAction.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.app.features.grouplist - -import im.vector.app.core.platform.VectorViewModelAction -import org.matrix.android.sdk.api.session.group.model.GroupSummary - -sealed class GroupListAction : VectorViewModelAction { - data class SelectGroup(val groupSummary: GroupSummary) : GroupListAction() -} diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListFragment.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListFragment.kt deleted file mode 100644 index 297e33c5830..00000000000 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListFragment.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.app.features.grouplist - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.airbnb.mvrx.Incomplete -import com.airbnb.mvrx.Success -import com.airbnb.mvrx.fragmentViewModel -import com.airbnb.mvrx.withState -import im.vector.app.core.extensions.cleanup -import im.vector.app.core.extensions.configureWith -import im.vector.app.core.extensions.exhaustive -import im.vector.app.core.platform.StateView -import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.databinding.FragmentGroupListBinding -import im.vector.app.features.home.HomeActivitySharedAction -import im.vector.app.features.home.HomeSharedActionViewModel - -import org.matrix.android.sdk.api.session.group.model.GroupSummary -import javax.inject.Inject - -class GroupListFragment @Inject constructor( - val groupListViewModelFactory: GroupListViewModel.Factory, - private val groupController: GroupSummaryController -) : VectorBaseFragment(), - GroupSummaryController.Callback { - - private lateinit var sharedActionViewModel: HomeSharedActionViewModel - private val viewModel: GroupListViewModel by fragmentViewModel() - - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGroupListBinding { - return FragmentGroupListBinding.inflate(inflater, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) - groupController.callback = this - views.stateView.contentView = views.groupListView - views.groupListView.configureWith(groupController) - viewModel.observeViewEvents { - when (it) { - is GroupListViewEvents.OpenGroupSummary -> sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup) - }.exhaustive - } - } - - override fun onDestroyView() { - groupController.callback = null - views.groupListView.cleanup() - super.onDestroyView() - } - - override fun invalidate() = withState(viewModel) { state -> - when (state.asyncGroups) { - is Incomplete -> views.stateView.state = StateView.State.Loading - is Success -> views.stateView.state = StateView.State.Content - } - groupController.update(state) - } - - override fun onGroupSelected(groupSummary: GroupSummary) { - viewModel.handle(GroupListAction.SelectGroup(groupSummary)) - } -} diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt deleted file mode 100644 index 0f6f77783df..00000000000 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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.app.features.grouplist - -import androidx.lifecycle.viewModelScope -import arrow.core.Option -import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.ViewModelContext -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import dagger.assisted.AssistedFactory -import im.vector.app.R -import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.resources.StringProvider -import io.reactivex.Observable -import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.query.QueryStringValue -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.group.groupSummaryQueryParams -import org.matrix.android.sdk.api.session.group.model.GroupSummary -import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.rx.rx - -const val ALL_COMMUNITIES_GROUP_ID = "+ALL_COMMUNITIES_GROUP_ID" - -class GroupListViewModel @AssistedInject constructor(@Assisted initialState: GroupListViewState, - private val selectedGroupStore: SelectedGroupDataSource, - private val session: Session, - private val stringProvider: StringProvider -) : VectorViewModel(initialState) { - - @AssistedFactory - interface Factory { - fun create(initialState: GroupListViewState): GroupListViewModel - } - - companion object : MvRxViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: GroupListViewState): GroupListViewModel? { - val groupListFragment: GroupListFragment = (viewModelContext as FragmentViewModelContext).fragment() - return groupListFragment.groupListViewModelFactory.create(state) - } - } - - private var currentGroupId = "" - - init { - observeGroupSummaries() - observeSelectionState() - } - - private fun observeSelectionState() { - selectSubscribe(GroupListViewState::selectedGroup) { groupSummary -> - if (groupSummary != null) { - // We only want to open group if the updated selectedGroup is a different one. - if (currentGroupId != groupSummary.groupId) { - currentGroupId = groupSummary.groupId - _viewEvents.post(GroupListViewEvents.OpenGroupSummary) - } - val optionGroup = Option.just(groupSummary) - selectedGroupStore.post(optionGroup) - } else { - // If selected group is null we force to default. It can happens when leaving the selected group. - setState { - copy(selectedGroup = this.asyncGroups()?.find { it.groupId == ALL_COMMUNITIES_GROUP_ID }) - } - } - } - } - - override fun handle(action: GroupListAction) { - when (action) { - is GroupListAction.SelectGroup -> handleSelectGroup(action) - } - } - - // PRIVATE METHODS ***************************************************************************** - - private fun handleSelectGroup(action: GroupListAction.SelectGroup) = withState { state -> - if (state.selectedGroup?.groupId != action.groupSummary.groupId) { - // We take care of refreshing group data when selecting to be sure we get all the rooms and users - tryOrNull { - viewModelScope.launch { - session.getGroup(action.groupSummary.groupId)?.fetchGroupData() - } - } - setState { copy(selectedGroup = action.groupSummary) } - } - } - - private fun observeGroupSummaries() { - val groupSummariesQueryParams = groupSummaryQueryParams { - memberships = listOf(Membership.JOIN) - displayName = QueryStringValue.IsNotEmpty - } - 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(groupSummariesQueryParams), - { allCommunityGroup, communityGroups -> - listOf(allCommunityGroup) + communityGroups - } - ) - .execute { async -> - val currentSelectedGroupId = selectedGroup?.groupId - val newSelectedGroup = if (currentSelectedGroupId != null) { - async()?.find { it.groupId == currentSelectedGroupId } - } else { - async()?.firstOrNull() - } - copy(asyncGroups = async, selectedGroup = newSelectedGroup) - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewState.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewState.kt deleted file mode 100644 index 4abcff2f671..00000000000 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewState.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.app.features.grouplist - -import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState -import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.session.group.model.GroupSummary - -data class GroupListViewState( - val asyncGroups: Async> = Uninitialized, - val selectedGroup: GroupSummary? = null -) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupSummaryController.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupSummaryController.kt deleted file mode 100644 index 03272c27293..00000000000 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupSummaryController.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.app.features.grouplist - -import com.airbnb.epoxy.EpoxyController -import im.vector.app.features.home.AvatarRenderer -import org.matrix.android.sdk.api.session.group.model.GroupSummary -import org.matrix.android.sdk.api.util.toMatrixItem -import javax.inject.Inject - -class GroupSummaryController @Inject constructor(private val avatarRenderer: AvatarRenderer) : EpoxyController() { - - var callback: Callback? = null - private var viewState: GroupListViewState? = null - - init { - requestModelBuild() - } - - fun update(viewState: GroupListViewState) { - this.viewState = viewState - requestModelBuild() - } - - override fun buildModels() { - val nonNullViewState = viewState ?: return - buildGroupModels(nonNullViewState.asyncGroups(), nonNullViewState.selectedGroup) - } - - private fun buildGroupModels(summaries: List?, selected: GroupSummary?) { - if (summaries.isNullOrEmpty()) { - return - } - summaries.forEach { groupSummary -> - val isSelected = groupSummary.groupId == selected?.groupId - groupSummaryItem { - avatarRenderer(avatarRenderer) - id(groupSummary.groupId) - matrixItem(groupSummary.toMatrixItem()) - selected(isSelected) - listener { callback?.onGroupSelected(groupSummary) } - } - } - } - - interface Callback { - fun onGroupSelected(groupSummary: GroupSummary) - } -} diff --git a/vector/src/main/java/im/vector/app/features/grouplist/SelectedGroupDataSource.kt b/vector/src/main/java/im/vector/app/features/grouplist/SelectedGroupDataSource.kt deleted file mode 100644 index 5a172e2636d..00000000000 --- a/vector/src/main/java/im/vector/app/features/grouplist/SelectedGroupDataSource.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.app.features.grouplist - -import arrow.core.Option -import im.vector.app.core.utils.BehaviorDataSource -import org.matrix.android.sdk.api.session.group.model.GroupSummary -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SelectedGroupDataSource @Inject constructor() : BehaviorDataSource>(Option.empty()) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 24ad6c8b855..5a7f2e2f8ff 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -32,6 +32,7 @@ import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel +import im.vector.app.AppStateHandler import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ScreenComponent @@ -108,6 +109,7 @@ class HomeActivity : @Inject lateinit var permalinkHandler: PermalinkHandler @Inject lateinit var avatarRenderer: AvatarRenderer @Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter + @Inject lateinit var appStateHandler: AppStateHandler private val createSpaceResultLauncher = registerStartForActivityResult { activityResult -> if (activityResult.resultCode == Activity.RESULT_OK) { @@ -152,10 +154,15 @@ class HomeActivity : sharedActionViewModel = viewModelProvider.get(HomeSharedActionViewModel::class.java) views.drawerLayout.addDrawerListener(drawerListener) if (isFirstCreation()) { - replaceFragment(R.id.homeDetailFragmentContainer, LoadingFragment::class.java) + replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java) replaceFragment(R.id.homeDrawerFragmentContainer, HomeDrawerFragment::class.java) } +// appStateHandler.selectedRoomGroupingObservable.subscribe { +// if (supportFragmentManager.getFragment()) +// replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true) +// }.disposeOnDestroy() + sharedActionViewModel .observe() .subscribe { sharedAction -> @@ -164,13 +171,17 @@ class HomeActivity : is HomeActivitySharedAction.CloseDrawer -> views.drawerLayout.closeDrawer(GravityCompat.START) is HomeActivitySharedAction.OpenGroup -> { views.drawerLayout.closeDrawer(GravityCompat.START) + // Temporary + // When switching from space to group or group to space, we need to reload the fragment + // To be removed when dropping legacy groups + if (sharedAction.clearFragment) { + replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true) + } else { + // nop + } // we might want to delay that to avoid having the drawer animation lagging // would be probably better to let the drawer do that? in the on closed callback? - views.coordinatorLayout.postDelayed({ - replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true) - }, 200) - Unit } is HomeActivitySharedAction.OpenSpacePreview -> { startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId)) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivitySharedAction.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivitySharedAction.kt index e87ce3476fa..db0a9ba9eb1 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivitySharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivitySharedAction.kt @@ -24,7 +24,7 @@ import im.vector.app.core.platform.VectorSharedAction sealed class HomeActivitySharedAction : VectorSharedAction { object OpenDrawer : HomeActivitySharedAction() object CloseDrawer : HomeActivitySharedAction() - object OpenGroup : HomeActivitySharedAction() + data class OpenGroup(val clearFragment: Boolean) : HomeActivitySharedAction() object AddSpace : HomeActivitySharedAction() data class OpenSpacePreview(val spaceId: String) : HomeActivitySharedAction() data class ShowSpaceSettings(val spaceId: String) : HomeActivitySharedAction() diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index e7831c72cc9..69395b23867 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -29,6 +29,7 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.badge.BadgeDrawable import im.vector.app.R +import im.vector.app.RoomGroupingMethod import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.core.platform.ToolbarConfigurable @@ -48,7 +49,6 @@ import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.popup.VerificationVectorAlert import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsActivity.Companion.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS -import im.vector.app.features.spaces.ALL_COMMUNITIES_GROUP_ID import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.workers.signout.BannerState import im.vector.app.features.workers.signout.ServerBackupStatusViewModel @@ -125,15 +125,14 @@ class HomeDetailFragment @Inject constructor( views.bottomNavigationView.selectedItemId = it.displayMode.toMenuId() } - viewModel.selectSubscribe(this, HomeDetailViewState::groupSummary) { groupSummary -> - if (!vectorPreferences.labSpaces()) { - onGroupChange(groupSummary.orNull()) - } - } - - viewModel.selectSubscribe(this, HomeDetailViewState::spaceSummary) { spaceSummary -> - if (vectorPreferences.labSpaces()) { - onSpaceChange(spaceSummary.orNull()) + viewModel.selectSubscribe(this, HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod -> + when (roomGroupingMethod) { + is RoomGroupingMethod.ByLegacyGroup -> { + onGroupChange(roomGroupingMethod.groupSummary) + } + is RoomGroupingMethod.BySpace -> { + onSpaceChange(roomGroupingMethod.spaceSummary) + } } } @@ -160,16 +159,12 @@ class HomeDetailFragment @Inject constructor( } unreadMessagesSharedViewModel.subscribe { state -> - if (vectorPreferences.labSpaces()) { - views.drawerUnreadCounterBadgeView.render( - UnreadCounterBadgeView.State( - count = state.otherSpacesUnread.totalCount, - highlighted = state.otherSpacesUnread.isHighlight - ) - ) - } else { - views.drawerUnreadCounterBadgeView.isVisible = false - } + views.drawerUnreadCounterBadgeView.render( + UnreadCounterBadgeView.State( + count = state.otherSpacesUnread.totalCount, + highlighted = state.otherSpacesUnread.isHighlight + ) + ) } sharedCallActionViewModel @@ -257,8 +252,7 @@ class HomeDetailFragment @Inject constructor( } private fun onGroupChange(groupSummary: GroupSummary?) { - groupSummary ?: return - if (groupSummary.groupId == ALL_COMMUNITIES_GROUP_ID) { + if (groupSummary == null) { views.groupToolbarSpaceTitleView.isVisible = false } else { views.groupToolbarSpaceTitleView.isVisible = true @@ -267,8 +261,7 @@ class HomeDetailFragment @Inject constructor( } private fun onSpaceChange(spaceSummary: RoomSummary?) { - spaceSummary ?: return - if (spaceSummary.roomId == ALL_COMMUNITIES_GROUP_ID) { + if (spaceSummary == null) { views.groupToolbarSpaceTitleView.isVisible = false } else { views.groupToolbarSpaceTitleView.isVisible = true @@ -310,11 +303,14 @@ class HomeDetailFragment @Inject constructor( views.homeToolbarContent.debouncedClicks { withState(viewModel) { - if (vectorPreferences.labSpaces()) { - val currentSpace = it.spaceSummary.orNull() - ?.takeIf { it.roomId != ALL_COMMUNITIES_GROUP_ID } - if (vectorPreferences.labSpaces() && currentSpace != null) { - sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(currentSpace.roomId)) + when (it.roomGroupingMethod) { + is RoomGroupingMethod.ByLegacyGroup -> { + // nothing do far + } + is RoomGroupingMethod.BySpace -> { + it.roomGroupingMethod.spaceSummary?.let { + sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(it.roomId)) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 1d57b260153..91f3877fab4 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -24,12 +24,12 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler +import im.vector.app.RoomGroupingMethod import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.resources.StringProvider -import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.ui.UiStateRepository +import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.ActiveSpaceFilter @@ -51,9 +51,7 @@ import java.util.concurrent.TimeUnit class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState, private val session: Session, private val uiStateRepository: UiStateRepository, - private val selectedGroupStore: SelectedGroupDataSource, - private val appStateHandler: AppStateHandler, - private val stringProvider: StringProvider) + private val appStateHandler: AppStateHandler) : VectorViewModel(initialState) { @AssistedFactory @@ -79,8 +77,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho init { observeSyncState() - observeSelectedGroupStore() - observeSelectedSpaceStore() + observeRoomGroupingMethod() observeRoomSummaries() session.rx().liveUser(session.myUserId).execute { @@ -138,29 +135,22 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho .disposeOnClear() } - private fun observeSelectedGroupStore() { - selectedGroupStore - .observe() + private fun observeRoomGroupingMethod() { + appStateHandler.selectedRoomGroupingObservable .subscribe { - setState { - copy(groupSummary = it) - } - } - .disposeOnClear() - } - - private fun observeSelectedSpaceStore() { - appStateHandler.selectedSpaceObservable - .subscribe { - setState { - copy(spaceSummary = it) - } + setState { + copy( + roomGroupingMethod = it.orNull() ?: RoomGroupingMethod.BySpace(null) + ) + } } .disposeOnClear() } private fun observeRoomSummaries() { - appStateHandler.selectedSpaceObservable.distinctUntilChanged().switchMap { + appStateHandler.selectedRoomGroupingObservable.distinctUntilChanged().switchMap { + // we use it as a trigger to all changes in room, but do not really load + // the actual models session.getPagedRoomSummariesLive( roomSummaryQueryParams { memberships = Membership.activeMemberships() @@ -168,50 +158,55 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho sortOrder = RoomSortOrder.NONE ).asObservable() } - -// .asObservable() + .observeOn(Schedulers.computation()) .throttleFirst(300, TimeUnit.MILLISECONDS) .subscribe { - val activeSpace = appStateHandler.safeActiveSpaceId() - val dmInvites = session.getRoomSummaries( - roomSummaryQueryParams { - memberships = listOf(Membership.INVITE) - roomCategoryFilter = RoomCategoryFilter.ONLY_DM - } - ).size - - val roomsInvite = session.getRoomSummaries( - roomSummaryQueryParams { - memberships = listOf(Membership.INVITE) - roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - } - ).size - - val dmRooms = session.getNotificationCountForRooms( - roomSummaryQueryParams { - memberships = listOf(Membership.JOIN) - roomCategoryFilter = RoomCategoryFilter.ONLY_DM + when (val groupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) { + is RoomGroupingMethod.ByLegacyGroup -> { + // TODO!! + } + is RoomGroupingMethod.BySpace -> { + val dmInvites = session.getRoomSummaries( + roomSummaryQueryParams { + memberships = listOf(Membership.INVITE) + roomCategoryFilter = RoomCategoryFilter.ONLY_DM + } + ).size + + val roomsInvite = session.getRoomSummaries( + roomSummaryQueryParams { + memberships = listOf(Membership.INVITE) + roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + } + ).size + + val dmRooms = session.getNotificationCountForRooms( + roomSummaryQueryParams { + memberships = listOf(Membership.JOIN) + roomCategoryFilter = RoomCategoryFilter.ONLY_DM + } + ) + + val otherRooms = session.getNotificationCountForRooms( + roomSummaryQueryParams { + memberships = listOf(Membership.JOIN) + roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + activeSpaceId = ActiveSpaceFilter.ActiveSpace(groupingMethod.spaceSummary?.roomId) + } + ) + + setState { + copy( + notificationCountCatchup = dmRooms.totalCount + otherRooms.totalCount + roomsInvite + dmInvites, + notificationHighlightCatchup = dmRooms.isHighlight || otherRooms.isHighlight, + notificationCountPeople = dmRooms.totalCount + dmInvites, + notificationHighlightPeople = dmRooms.isHighlight || dmInvites > 0, + notificationCountRooms = otherRooms.totalCount + roomsInvite, + notificationHighlightRooms = otherRooms.isHighlight || roomsInvite > 0, + hasUnreadMessages = dmRooms.totalCount + otherRooms.totalCount > 0 + ) } - ) - - val otherRooms = session.getNotificationCountForRooms( - roomSummaryQueryParams { - memberships = listOf(Membership.JOIN) - roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - activeSpaceId = ActiveSpaceFilter.ActiveSpace(activeSpace) - } - ) - - setState { - copy( - notificationCountCatchup = dmRooms.totalCount + otherRooms.totalCount + roomsInvite + dmInvites, - notificationHighlightCatchup = dmRooms.isHighlight || otherRooms.isHighlight, - notificationCountPeople = dmRooms.totalCount + dmInvites, - notificationHighlightPeople = dmRooms.isHighlight || dmInvites > 0, - notificationCountRooms = otherRooms.totalCount + roomsInvite, - notificationHighlightRooms = otherRooms.isHighlight || roomsInvite > 0, - hasUnreadMessages = dmRooms.totalCount + otherRooms.totalCount > 0 - ) + } } } .disposeOnClear() diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt index 637182d0520..5aa9612a7a1 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt @@ -16,18 +16,16 @@ package im.vector.app.features.home -import arrow.core.Option import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.session.group.model.GroupSummary +import im.vector.app.RoomGroupingMethod import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.util.MatrixItem data class HomeDetailViewState( - val groupSummary: Option = Option.empty(), - val spaceSummary: Option = Option.empty(), + val roomGroupingMethod: RoomGroupingMethod = RoomGroupingMethod.BySpace(null), val myMatrixItem: MatrixItem? = null, val asyncRooms: Async> = Uninitialized, val displayMode: RoomListDisplayMode = RoomListDisplayMode.PEOPLE, diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt index 91811ffb137..9ff865b9d17 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt @@ -30,7 +30,7 @@ import im.vector.app.core.extensions.replaceChildFragment import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentHomeDrawerBinding -import im.vector.app.features.grouplist.GroupListFragment +// import im.vector.app.features.grouplist.GroupListFragment import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.spaces.SpaceListFragment @@ -59,11 +59,7 @@ class HomeDrawerFragment @Inject constructor( sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) if (savedInstanceState == null) { - if (vectorPreferences.labSpaces()) { - replaceChildFragment(R.id.homeDrawerGroupListContainer, SpaceListFragment::class.java) - } else { - replaceChildFragment(R.id.homeDrawerGroupListContainer, GroupListFragment::class.java) - } + replaceChildFragment(R.id.homeDrawerGroupListContainer, SpaceListFragment::class.java) } session.getUserLive(session.myUserId).observeK(viewLifecycleOwner) { optionalUser -> val user = optionalUser?.getOrNull() diff --git a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt index 85440c65821..cfafd1f103a 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt @@ -25,10 +25,10 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler +import im.vector.app.RoomGroupingMethod import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.settings.VectorPreferences import io.reactivex.Observable import io.reactivex.functions.BiFunction import io.reactivex.schedulers.Schedulers @@ -53,8 +53,7 @@ data class CountInfo( class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initialState: UnreadMessagesState, session: Session, - appStateHandler: AppStateHandler, - private val vectorPreferences: VectorPreferences) + appStateHandler: AppStateHandler) : VectorViewModel(initialState) { @AssistedFactory @@ -92,10 +91,12 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) } ) - val invites = session.getRoomSummaries(roomSummaryQueryParams { - this.memberships = listOf(Membership.INVITE) - this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) - }).size + val invites = session.getRoomSummaries( + roomSummaryQueryParams { + this.memberships = listOf(Membership.INVITE) + this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) + } + ).size setState { copy( homeSpaceUnread = RoomAggregateNotificationCount( @@ -107,8 +108,8 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia }.disposeOnClear() Observable.combineLatest( - appStateHandler.selectedSpaceObservable.distinctUntilChanged(), - appStateHandler.selectedSpaceObservable.switchMap { + appStateHandler.selectedRoomGroupingObservable.distinctUntilChanged(), + appStateHandler.selectedRoomGroupingObservable.switchMap { session.getPagedRoomSummariesLive( roomSummaryQueryParams { this.memberships = Membership.activeMemberships() @@ -117,30 +118,47 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia .throttleFirst(300, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) }, - BiFunction { _, _ -> - val selectedSpace = appStateHandler.safeActiveSpaceId() - val counts = session.getNotificationCountForRooms( - roomSummaryQueryParams { - this.memberships = listOf(Membership.JOIN) - this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) - } - ) - val rootCounts = session.spaceService().getRootSpaceSummaries() - .filter { - // filter out current selection - it.roomId != selectedSpace - } - CountInfo( - homeCount = counts, - otherCount = RoomAggregateNotificationCount( - rootCounts.fold(0, { acc, rs -> - acc + rs.notificationCount - }) + (counts.notificationCount.takeIf { selectedSpace != null } ?: 0), - rootCounts.fold(0, { acc, rs -> - acc + rs.highlightCount - }) + (counts.highlightCount.takeIf { selectedSpace != null } ?: 0) + BiFunction { groupingMethod, _ -> + when (groupingMethod.orNull()) { + is RoomGroupingMethod.ByLegacyGroup -> { + // currently not supported + CountInfo( + RoomAggregateNotificationCount(0, 0), + RoomAggregateNotificationCount(0, 0) ) - ) + } + is RoomGroupingMethod.BySpace -> { + val selectedSpace = appStateHandler.safeActiveSpaceId() + val counts = session.getNotificationCountForRooms( + roomSummaryQueryParams { + this.memberships = listOf(Membership.JOIN) + this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) + } + ) + val rootCounts = session.spaceService().getRootSpaceSummaries() + .filter { + // filter out current selection + it.roomId != selectedSpace + } + CountInfo( + homeCount = counts, + otherCount = RoomAggregateNotificationCount( + rootCounts.fold(0, { acc, rs -> + acc + rs.notificationCount + }) + (counts.notificationCount.takeIf { selectedSpace != null } ?: 0), + rootCounts.fold(0, { acc, rs -> + acc + rs.highlightCount + }) + (counts.highlightCount.takeIf { selectedSpace != null } ?: 0) + ) + ) + } + null -> { + CountInfo( + RoomAggregateNotificationCount(0, 0), + RoomAggregateNotificationCount(0, 0) + ) + } + } } ).execute { copy( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 0b5c5200927..0721f81cf4e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -178,14 +178,14 @@ class RoomDetailViewModel @AssistedInject constructor( observePowerLevel() updateShowDialerOptionState() room.getRoomSummaryLive() - viewModelScope.launch { + viewModelScope.launch(Dispatchers.IO) { tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) } } // Inform the SDK that the room is displayed - viewModelScope.launch { + viewModelScope.launch(Dispatchers.IO) { try { session.onRoomDisplayed(initialState.roomId) - } catch (_: Exception) { + } catch (_: Throwable) { } } callManager.addPstnSupportListener(this) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt index 7c994666a94..22b0eb091c2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt @@ -17,10 +17,10 @@ package im.vector.app.features.home.room.list import androidx.annotation.StringRes +import im.vector.app.AppStateHandler import im.vector.app.R +import im.vector.app.RoomGroupingMethod import im.vector.app.core.resources.StringProvider -import im.vector.app.features.grouplist.ALL_COMMUNITIES_GROUP_ID -import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.home.RoomListDisplayMode import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers @@ -37,25 +37,24 @@ class GroupRoomListSectionBuilder( val session: Session, val stringProvider: StringProvider, val viewModelScope: CoroutineScope, - val selectedGroupDataSource: SelectedGroupDataSource, + val appStateHandler: AppStateHandler, val onDisposable: (Disposable) -> Unit, val onUdpatable: (UpdatableLivePageResult) -> Unit ) : RoomListSectionBuilder { override fun buildSections(mode: RoomListDisplayMode): List { - val activeSpaceAwareQueries = mutableListOf() + val activeGroupAwareQueries = mutableListOf() val sections = mutableListOf() - val actualGroupId = selectedGroupDataSource.currentValue?.orNull() - ?.groupId?.takeIf { it != ALL_COMMUNITIES_GROUP_ID } + val actualGroupId = appStateHandler.safeActiveGroupId() when (mode) { RoomListDisplayMode.PEOPLE -> { // 3 sections Invites / Fav / Dms - buildPeopleSections(sections, activeSpaceAwareQueries, actualGroupId) + buildPeopleSections(sections, activeGroupAwareQueries, actualGroupId) } RoomListDisplayMode.ROOMS -> { // 5 sections invites / Fav / Rooms / Low Priority / Server notice - buildRoomsSections(sections, activeSpaceAwareQueries, actualGroupId) + buildRoomsSections(sections, activeGroupAwareQueries, actualGroupId) } RoomListDisplayMode.FILTERED -> { // Used when searching for rooms @@ -76,7 +75,7 @@ class GroupRoomListSectionBuilder( RoomListDisplayMode.NOTIFICATIONS -> { addSection( sections, - activeSpaceAwareQueries, + activeGroupAwareQueries, R.string.invitations_header, true ) { @@ -87,7 +86,7 @@ class GroupRoomListSectionBuilder( addSection( sections, - activeSpaceAwareQueries, + activeGroupAwareQueries, R.string.bottom_action_rooms, false ) { @@ -98,11 +97,11 @@ class GroupRoomListSectionBuilder( } } - selectedGroupDataSource.observe() + appStateHandler.selectedRoomGroupingObservable .distinctUntilChanged() - .subscribe { activeSpaceOption -> - val selectedGroupId = activeSpaceOption.orNull()?.groupId?.takeIf { it != ALL_COMMUNITIES_GROUP_ID } - activeSpaceAwareQueries.onEach { updater -> + .subscribe { groupingMethod -> + val selectedGroupId = (groupingMethod.orNull() as? RoomGroupingMethod.ByLegacyGroup)?.groupSummary?.groupId + activeGroupAwareQueries.onEach { updater -> updater.updateQuery { query -> query.copy(activeGroupId = selectedGroupId) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 7f213336f8c..1044d598c8d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -26,10 +26,10 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import im.vector.app.AppStateHandler +import im.vector.app.RoomGroupingMethod import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -49,7 +49,6 @@ class RoomListViewModel @Inject constructor( private val session: Session, private val stringProvider: StringProvider, private val appStateHandler: AppStateHandler, - private val selectedGroupDataSource: SelectedGroupDataSource, private val vectorPreferences: VectorPreferences ) : VectorViewModel(initialState) { @@ -74,13 +73,11 @@ class RoomListViewModel @Inject constructor( init { observeMembershipChanges() - appStateHandler.selectedSpaceObservable - .distinctUntilChanged() - .map { it.orNull() } + appStateHandler.selectedRoomGroupingObservable .distinctUntilChanged() .execute { copy( - currentSpace = it + currentRoomGrouping = it.invoke()?.orNull()?.let { Success(it) } ?: Loading() ) } @@ -113,7 +110,7 @@ class RoomListViewModel @Inject constructor( } val sections: List by lazy { - if (vectorPreferences.labSpaces()) { + if (appStateHandler.getCurrentRoomGroupingMethod() is RoomGroupingMethod.BySpace) { SpaceRoomListSectionBuilder( session, stringProvider, @@ -132,7 +129,7 @@ class RoomListViewModel @Inject constructor( session, stringProvider, viewModelScope, - selectedGroupDataSource, + appStateHandler, { it.disposeOnClear() }, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt index 0a142304a0b..a30c175f417 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt @@ -18,7 +18,6 @@ package im.vector.app.features.home.room.list import im.vector.app.AppStateHandler import im.vector.app.core.resources.StringProvider -import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.settings.VectorPreferences import org.matrix.android.sdk.api.session.Session import javax.inject.Inject @@ -27,8 +26,7 @@ import javax.inject.Provider class RoomListViewModelFactory @Inject constructor(private val session: Provider, private val appStateHandler: AppStateHandler, private val stringProvider: StringProvider, - private val vectorPreferences: VectorPreferences, - private val selectedGroupDataSource: SelectedGroupDataSource) + private val vectorPreferences: VectorPreferences) : RoomListViewModel.Factory { override fun create(initialState: RoomListViewState): RoomListViewModel { @@ -37,7 +35,6 @@ class RoomListViewModelFactory @Inject constructor(private val session: Provider session.get(), stringProvider, appStateHandler, - selectedGroupDataSource, vectorPreferences ) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt index d18574d75ba..68a8b9e5156 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt @@ -19,9 +19,9 @@ package im.vector.app.features.home.room.list import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.RoomGroupingMethod import im.vector.app.features.home.RoomListDisplayMode import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState -import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo data class RoomListViewState( @@ -30,7 +30,7 @@ data class RoomListViewState( val roomMembershipChanges: Map = emptyMap(), val asyncSuggestedRooms: Async> = Uninitialized, val currentUserName: String? = null, - val currentSpace: Async = Uninitialized + val currentRoomGrouping: Async = Uninitialized ) : MvRxState { constructor(args: RoomListParams) : this(displayMode = args.displayMode) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt index 353cf5732d4..7edd2afccf7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt @@ -26,13 +26,13 @@ import im.vector.app.AppStateHandler import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.RoomListDisplayMode +import im.vector.app.space import io.reactivex.Observable import io.reactivex.disposables.Disposable import io.reactivex.rxkotlin.Observables import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter @@ -110,12 +110,12 @@ class SpaceRoomListSectionBuilder( } } - appStateHandler.selectedSpaceObservable + appStateHandler.selectedRoomGroupingObservable .distinctUntilChanged() - .subscribe { activeSpaceOption -> - val selectedSpace = activeSpaceOption.orNull() + .subscribe { groupingMethod -> + val selectedSpace = groupingMethod.orNull()?.space() activeSpaceAwareQueries.onEach { updater -> - updater.updateForSpaceId(selectedSpace?.roomId?.takeIf { MatrixPatterns.isRoomId(it) }) + updater.updateForSpaceId(selectedSpace?.roomId) } }.also { onDisposable.invoke(it) @@ -185,10 +185,10 @@ class SpaceRoomListSectionBuilder( // add suggested rooms val suggestedRoomsObservable = // MutableLiveData>() - appStateHandler.selectedSpaceObservable + appStateHandler.selectedRoomGroupingObservable .distinctUntilChanged() - .switchMap { activeSpaceOption -> - val selectedSpace = activeSpaceOption.orNull() + .switchMap { groupingMethod -> + val selectedSpace = groupingMethod.orNull()?.space() if (selectedSpace == null) { Observable.just(emptyList()) } else { diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index c033cc40fa3..73d8325bcaf 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -31,6 +31,7 @@ import androidx.core.util.Pair import androidx.core.view.ViewCompat import im.vector.app.AppStateHandler import im.vector.app.R +import im.vector.app.RoomGroupingMethod import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.fatalError import im.vector.app.core.platform.VectorBaseActivity @@ -76,13 +77,13 @@ import im.vector.app.features.spaces.SpacePreviewActivity import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.widgets.WidgetActivity import im.vector.app.features.widgets.WidgetArgsBuilder +import im.vector.app.space import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.WidgetType -import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -106,17 +107,11 @@ class DefaultNavigator @Inject constructor( } override fun switchToSpace(context: Context, spaceId: String, roomId: String?, openShareSheet: Boolean) { - if (sessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(spaceId) == null) { + if (sessionHolder.getSafeActiveSession()?.getRoomSummary(spaceId) == null) { fatalError("Trying to open an unknown space $spaceId", vectorPreferences.failFast()) return } - - sessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(spaceId)?.spaceSummary()?.let { - Timber.d("## Nav: Switching to space $spaceId / ${it.name}") - appStateHandler.setCurrentSpace(it) - } ?: kotlin.run { - Timber.d("## Nav: Failed to switch to space $spaceId") - } + appStateHandler.setCurrentSpace(spaceId) if (roomId != null) { val args = RoomDetailArgs(roomId, eventId = null, openShareSpaceForId = spaceId.takeIf { openShareSheet }) val intent = RoomDetailActivity.newIntent(context, args) @@ -251,15 +246,22 @@ class DefaultNavigator @Inject constructor( } override fun openRoomDirectory(context: Context, initialFilter: String) { - val selectedSpace = appStateHandler.safeActiveSpace()?.let { - sessionHolder.getSafeActiveSession()?.getRoomSummary(it.roomId) - } - if (selectedSpace == null) { - val intent = RoomDirectoryActivity.getIntent(context, initialFilter) - context.startActivity(intent) - } else { - SpaceExploreActivity.newIntent(context, selectedSpace.roomId).let { - context.startActivity(it) + when (val groupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) { + is RoomGroupingMethod.ByLegacyGroup -> { + // TODO should open list of rooms of this group + val intent = RoomDirectoryActivity.getIntent(context, initialFilter) + context.startActivity(intent) + } + is RoomGroupingMethod.BySpace -> { + val selectedSpace = groupingMethod.space() + if (selectedSpace == null) { + val intent = RoomDirectoryActivity.getIntent(context, initialFilter) + context.startActivity(intent) + } else { + SpaceExploreActivity.newIntent(context, selectedSpace.roomId).let { + context.startActivity(it) + } + } } } } @@ -275,29 +277,36 @@ class DefaultNavigator @Inject constructor( } override fun openInviteUsersToRoom(context: Context, roomId: String) { - val selectedSpace = appStateHandler.safeActiveSpace() - if (vectorPreferences.labSpaces() && selectedSpace != null) { - // let user decides if he does it from space or room - (context as? AppCompatActivity)?.supportFragmentManager?.let { fm -> - InviteRoomSpaceChooserBottomSheet.newInstance( - selectedSpace.roomId, - roomId, - object : InviteRoomSpaceChooserBottomSheet.InteractionListener { - override fun inviteToSpace(spaceId: String) { - val intent = InviteUsersToRoomActivity.getIntent(context, spaceId) - context.startActivity(intent) - } - - override fun inviteToRoom(roomId: String) { - val intent = InviteUsersToRoomActivity.getIntent(context, roomId) - context.startActivity(intent) - } - } - ).show(fm, InviteRoomSpaceChooserBottomSheet::class.java.name) + when (val currentGroupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) { + is RoomGroupingMethod.ByLegacyGroup -> { + val intent = InviteUsersToRoomActivity.getIntent(context, roomId) + context.startActivity(intent) + } + is RoomGroupingMethod.BySpace -> { + if (currentGroupingMethod.spaceSummary != null) { + // let user decides if he does it from space or room + (context as? AppCompatActivity)?.supportFragmentManager?.let { fm -> + InviteRoomSpaceChooserBottomSheet.newInstance( + currentGroupingMethod.spaceSummary.roomId, + roomId, + object : InviteRoomSpaceChooserBottomSheet.InteractionListener { + override fun inviteToSpace(spaceId: String) { + val intent = InviteUsersToRoomActivity.getIntent(context, spaceId) + context.startActivity(intent) + } + + override fun inviteToRoom(roomId: String) { + val intent = InviteUsersToRoomActivity.getIntent(context, roomId) + context.startActivity(intent) + } + } + ).show(fm, InviteRoomSpaceChooserBottomSheet::class.java.name) + } + } else { + val intent = InviteUsersToRoomActivity.getIntent(context, roomId) + context.startActivity(intent) + } } - } else { - val intent = InviteUsersToRoomActivity.getIntent(context, roomId) - context.startActivity(intent) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index b2b609d7145..2754d3fab06 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -20,12 +20,10 @@ import android.media.RingtoneManager import android.net.Uri import android.provider.MediaStore import androidx.core.content.edit -import androidx.lifecycle.LiveData import com.squareup.seismic.ShakeDetector import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.di.DefaultSharedPreferences -import im.vector.app.core.platform.livedata.SharedPreferenceLiveData import im.vector.app.features.disclaimer.SHARED_PREF_KEY import im.vector.app.features.homeserver.ServerUrlsRepository import im.vector.app.features.themes.ThemeUtils @@ -310,18 +308,6 @@ class VectorPreferences @Inject constructor(private val context: Context) { return defaultPrefs.getBoolean(SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB, false) } - fun labSpaces(): Boolean { - return defaultPrefs.getBoolean(SETTINGS_LABS_USE_SPACES, false) - } - - fun labSpacesLive(): LiveData { - return SharedPreferenceLiveData.booleanLiveData( - defaultPrefs, - SETTINGS_LABS_USE_SPACES, - false - ) - } - fun failFast(): Boolean { return BuildConfig.DEBUG || (developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, false)) } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt index cc2ca4eb4d9..6a4ea83d684 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt @@ -17,9 +17,6 @@ package im.vector.app.features.settings import im.vector.app.R -import im.vector.app.core.preference.VectorSwitchPreference -import im.vector.app.features.MainActivity -import im.vector.app.features.MainActivityArgs import javax.inject.Inject class VectorSettingsLabsFragment @Inject constructor( @@ -30,11 +27,6 @@ class VectorSettingsLabsFragment @Inject constructor( override val preferenceXmlRes = R.xml.vector_settings_labs override fun bindPref() { - findPreference(VectorPreferences.SETTINGS_LABS_USE_SPACES)!!.let { pref -> - pref.setOnPreferenceChangeListener { _, _ -> - MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCache = false)) - true - } - } + // Nothing to do } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceAddItem.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceAddItem.kt new file mode 100644 index 00000000000..703ba737909 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceAddItem.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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.app.features.spaces + +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.utils.DebouncedClickListener + +@EpoxyModelClass(layout = R.layout.item_space_add) +abstract class SpaceAddItem : VectorEpoxyModel() { + + @EpoxyAttribute var listener: (() -> Unit)? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.view.setOnClickListener( + DebouncedClickListener({ + listener?.invoke() + }) + ) + } + + class Holder : VectorEpoxyHolder() +} diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceBetaHeaderItem.kt similarity index 57% rename from vector/src/main/java/im/vector/app/features/grouplist/GroupListViewEvents.kt rename to vector/src/main/java/im/vector/app/features/spaces/SpaceBetaHeaderItem.kt index 7cd12b83649..8014dfad3d8 100644 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceBetaHeaderItem.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright (c) 2021 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. @@ -14,13 +14,15 @@ * limitations under the License. */ -package im.vector.app.features.grouplist +package im.vector.app.features.spaces -import im.vector.app.core.platform.VectorViewEvents +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel -/** - * Transient events for group list screen - */ -sealed class GroupListViewEvents : VectorViewEvents { - object OpenGroupSummary : GroupListViewEvents() +@EpoxyModelClass(layout = R.layout.item_space_beta_header) +abstract class SpaceBetaHeaderItem : VectorEpoxyModel() { + + class Holder : VectorEpoxyHolder() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt index 58812bd77e6..b8b6cb46670 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt @@ -17,12 +17,15 @@ package im.vector.app.features.spaces import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary sealed class SpaceListAction : VectorViewModelAction { - data class SelectSpace(val spaceSummary: RoomSummary) : SpaceListAction() + data class SelectSpace(val spaceSummary: RoomSummary?) : SpaceListAction() data class OpenSpaceInvite(val spaceSummary: RoomSummary) : SpaceListAction() data class LeaveSpace(val spaceSummary: RoomSummary) : SpaceListAction() data class ToggleExpand(val spaceSummary: RoomSummary) : SpaceListAction() object AddSpace : SpaceListAction() + + data class SelectLegacyGroup(val groupSummary: GroupSummary?) : SpaceListAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt index 6455b18d131..672fb289283 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt @@ -32,6 +32,7 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGroupListBinding import im.vector.app.features.home.HomeActivitySharedAction import im.vector.app.features.home.HomeSharedActionViewModel +import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject @@ -56,8 +57,9 @@ class SpaceListFragment @Inject constructor( viewModel.observeViewEvents { when (it) { is SpaceListViewEvents.OpenSpaceSummary -> sharedActionViewModel.post(HomeActivitySharedAction.OpenSpacePreview(it.id)) - is SpaceListViewEvents.OpenSpace -> sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup) - is SpaceListViewEvents.AddSpace -> sharedActionViewModel.post(HomeActivitySharedAction.AddSpace) + is SpaceListViewEvents.OpenSpace -> sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup(it.groupingMethodHasChanged)) + is SpaceListViewEvents.AddSpace -> sharedActionViewModel.post(HomeActivitySharedAction.AddSpace) + is SpaceListViewEvents.OpenGroup -> sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup(it.groupingMethodHasChanged)) }.exhaustive } } @@ -76,7 +78,7 @@ class SpaceListFragment @Inject constructor( spaceController.update(state) } - override fun onSpaceSelected(spaceSummary: RoomSummary) { + override fun onSpaceSelected(spaceSummary: RoomSummary?) { viewModel.handle(SpaceListAction.SelectSpace(spaceSummary)) } @@ -94,4 +96,8 @@ class SpaceListFragment @Inject constructor( override fun onAddSpaceSelected() { viewModel.handle(SpaceListAction.AddSpace) } + + override fun onGroupSelected(groupSummary: GroupSummary?) { + viewModel.handle(SpaceListAction.SelectLegacyGroup(groupSummary)) + } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewEvents.kt index 7f05172cd10..b7e31d28f23 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewEvents.kt @@ -22,7 +22,8 @@ import im.vector.app.core.platform.VectorViewEvents * Transient events for group list screen */ sealed class SpaceListViewEvents : VectorViewEvents { - object OpenSpace : SpaceListViewEvents() + data class OpenSpace(val groupingMethodHasChanged: Boolean) : SpaceListViewEvents() data class OpenSpaceSummary(val id: String) : SpaceListViewEvents() object AddSpace : SpaceListViewEvents() + data class OpenGroup(val groupingMethodHasChanged: Boolean) : SpaceListViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt index 558b631c160..b57a5d364b0 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt @@ -19,13 +19,18 @@ package im.vector.app.features.spaces import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.RoomGroupingMethod +import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount +import org.matrix.android.sdk.api.util.MatrixItem data class SpaceListViewState( + val myMxItem : Async = Uninitialized, val asyncSpaces: Async> = Uninitialized, - val selectedSpace: RoomSummary? = null, + val selectedGroupingMethod: RoomGroupingMethod = RoomGroupingMethod.BySpace(null), val rootSpaces: List? = null, + val legacyGroups: List? = null, val expandedStates: Map = emptyMap(), val homeAggregateCount : RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0) ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt index 8b30687a128..c3e730f29e9 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt @@ -18,14 +18,17 @@ package im.vector.app.features.spaces import com.airbnb.epoxy.EpoxyController import im.vector.app.R +import im.vector.app.RoomGroupingMethod import im.vector.app.core.resources.StringProvider -import im.vector.app.core.ui.list.genericButtonItem import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericItemHeader -import im.vector.app.core.utils.DebouncedClickListener +import im.vector.app.features.grouplist.groupSummaryItem import im.vector.app.features.grouplist.homeSpaceSummaryItem import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.list.UnreadCounterBadgeView +import im.vector.app.group +import im.vector.app.space +import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount @@ -53,63 +56,97 @@ class SpaceSummaryController @Inject constructor( val nonNullViewState = viewState ?: return buildGroupModels( nonNullViewState.asyncSpaces(), - nonNullViewState.selectedSpace, + nonNullViewState.selectedGroupingMethod, nonNullViewState.rootSpaces, nonNullViewState.expandedStates, nonNullViewState.homeAggregateCount) + + if (!nonNullViewState.legacyGroups.isNullOrEmpty()) { + genericFooterItem { + id("legacy_space") + text(" ") + } + + genericItemHeader { + id("legacy_groups") + text(stringProvider.getString(R.string.groups_header)) + } + + // add home for communities + nonNullViewState.myMxItem.invoke()?.let { mxItem -> + groupSummaryItem { + avatarRenderer(avatarRenderer) + id("all_communities") + matrixItem(mxItem.copy(displayName = stringProvider.getString(R.string.group_all_communities))) + selected(nonNullViewState.selectedGroupingMethod is RoomGroupingMethod.ByLegacyGroup + && nonNullViewState.selectedGroupingMethod.group() == null) + listener { callback?.onGroupSelected(null) } + } + } + + nonNullViewState.legacyGroups.forEach { groupSummary -> + groupSummaryItem { + avatarRenderer(avatarRenderer) + id(groupSummary.groupId) + matrixItem(groupSummary.toMatrixItem()) + selected(nonNullViewState.selectedGroupingMethod is RoomGroupingMethod.ByLegacyGroup + && nonNullViewState.selectedGroupingMethod.group()?.groupId == groupSummary.groupId) + listener { callback?.onGroupSelected(groupSummary) } + } + } + } } private fun buildGroupModels(summaries: List?, - selected: RoomSummary?, + selected: RoomGroupingMethod, rootSpaces: List?, expandedStates: Map, homeCount: RoomAggregateNotificationCount) { if (summaries.isNullOrEmpty()) { return } + + genericItemHeader { + id("spaces") + text(stringProvider.getString(R.string.spaces_header)) + } + + spaceBetaHeaderItem { id("beta_header") } + // show invites on top summaries.filter { it.membership == Membership.INVITE } .let { invites -> if (invites.isNotEmpty()) { - genericItemHeader { - id("invites") - text(stringProvider.getString(R.string.spaces_invited_header)) - } +// genericItemHeader { +// id("invites") +// text(stringProvider.getString(R.string.spaces_invited_header)) +// } invites.forEach { spaceSummaryItem { avatarRenderer(avatarRenderer) id(it.roomId) matrixItem(it.toMatrixItem()) + countState(UnreadCounterBadgeView.State(1, true)) selected(false) + description(stringProvider.getString(R.string.you_are_invited)) listener { callback?.onSpaceInviteSelected(it) } } } - genericFooterItem { - id("invite_space") - text("") - } } } - genericItemHeader { - id("spaces") - text(stringProvider.getString(R.string.spaces_header)) + homeSpaceSummaryItem { + id("space_home") + selected(selected is RoomGroupingMethod.BySpace && selected.space() == null) + countState(UnreadCounterBadgeView.State(homeCount.totalCount, homeCount.isHighlight)) + listener { callback?.onSpaceSelected(null) } } - summaries.firstOrNull { it.roomId == ALL_COMMUNITIES_GROUP_ID } - ?.let { - homeSpaceSummaryItem { - id(it.roomId) - selected(it.roomId == selected?.roomId) - countState(UnreadCounterBadgeView.State(homeCount.totalCount, homeCount.isHighlight)) - listener { callback?.onSpaceSelected(it) } - } - } - rootSpaces + ?.sortedBy { it.displayName } ?.forEach { groupSummary -> - val isSelected = groupSummary.roomId == selected?.roomId + val isSelected = selected is RoomGroupingMethod.BySpace && groupSummary.roomId == selected.space()?.roomId // does it have children? val subSpaces = groupSummary.children?.filter { childInfo -> summaries.indexOfFirst { it.roomId == childInfo.childRoomId } != -1 @@ -139,7 +176,7 @@ class SpaceSummaryController @Inject constructor( // it's expanded subSpaces?.forEach { child -> summaries.firstOrNull { it.roomId == child.childRoomId }?.let { childSum -> - val isChildSelected = childSum.roomId == selected?.roomId + val isChildSelected = selected is RoomGroupingMethod.BySpace && childSum.roomId == selected.space()?.roomId spaceSummaryItem { avatarRenderer(avatarRenderer) id(child.childRoomId) @@ -161,20 +198,19 @@ class SpaceSummaryController @Inject constructor( } // Temporary item to create a new Space (will move with final design) - - genericButtonItem { + spaceAddItem { id("create") - text(stringProvider.getString(R.string.add_space)) - iconRes(R.drawable.ic_add_black) - buttonClickAction(DebouncedClickListener({ callback?.onAddSpaceSelected() })) + listener { callback?.onAddSpaceSelected() } } } interface Callback { - fun onSpaceSelected(spaceSummary: RoomSummary) + fun onSpaceSelected(spaceSummary: RoomSummary?) fun onSpaceInviteSelected(spaceSummary: RoomSummary) fun onSpaceSettings(spaceSummary: RoomSummary) fun onToggleExpand(spaceSummary: RoomSummary) fun onAddSpaceSelected() + + fun onGroupSelected(groupSummary: GroupSummary?) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryItem.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryItem.kt index 797087e78be..105cb0dd576 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryItem.kt @@ -27,6 +27,7 @@ import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.CheckableConstraintLayout import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.features.home.AvatarRenderer @@ -46,6 +47,7 @@ abstract class SpaceSummaryItem : VectorEpoxyModel() { @EpoxyAttribute var hasChildren: Boolean = false @EpoxyAttribute var indent: Int = 0 @EpoxyAttribute var countState : UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false) + @EpoxyAttribute var description: String? = null override fun bind(holder: Holder) { super.bind(holder) @@ -62,6 +64,7 @@ abstract class SpaceSummaryItem : VectorEpoxyModel() { holder.moreView.isVisible = false } + holder.secondLineText.setTextOrHide(description) if (hasChildren) { // holder.collapseIndicator.setOnClickListener( // DebouncedClickListener({ _ -> @@ -104,6 +107,7 @@ abstract class SpaceSummaryItem : VectorEpoxyModel() { class Holder : VectorEpoxyHolder() { val avatarImageView by bind(R.id.groupAvatarImageView) val groupNameView by bind(R.id.groupNameView) + val secondLineText by bind(R.id.groupDescView) val rootView by bind(R.id.itemGroupLayout) val moreView by bind(R.id.groupTmpLeave) val collapseIndicator by bind(R.id.groupChildrenCollapse) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index de8e8bbc95d..90ab7713428 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -18,16 +18,20 @@ package im.vector.app.features.spaces import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler -import im.vector.app.R +import im.vector.app.RoomGroupingMethod import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.ui.UiStateRepository +import im.vector.app.group +import im.vector.app.space import io.reactivex.Observable import io.reactivex.functions.BiFunction import io.reactivex.schedulers.Schedulers @@ -36,16 +40,17 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.group.groupSummaryQueryParams import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.rx import java.util.concurrent.TimeUnit -const val ALL_COMMUNITIES_GROUP_ID = "+ALL_COMMUNITIES_GROUP_ID" - class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, private val appStateHandler: AppStateHandler, private val session: Session, @@ -67,23 +72,42 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp } } - private var currentGroupId = "" +// private var currentGroupingMethod : RoomGroupingMethod? = null init { + + session.getUserLive(session.myUserId).asObservable() + .subscribe { + setState { + copy( + myMxItem = it?.getOrNull()?.toMatrixItem()?.let { Success(it) } ?: Loading() + ) + } + }.disposeOnClear() + observeSpaceSummaries() - observeSelectionState() - appStateHandler.selectedSpaceObservable +// observeSelectionState() + appStateHandler.selectedRoomGroupingObservable + .distinctUntilChanged() .subscribe { - if (currentGroupId != it.orNull()?.roomId) { - setState { - copy( - selectedSpace = it.orNull() - ) - } + setState { + copy( + selectedGroupingMethod = it.orNull() ?: RoomGroupingMethod.BySpace(null) + ) } } .disposeOnClear() + session.getGroupSummariesLive(groupSummaryQueryParams {}) + .asObservable() + .subscribe { + setState { + copy( + legacyGroups = it + ) + } + }.disposeOnClear() + session.getPagedRoomSummariesLive( roomSummaryQueryParams { this.memberships = listOf(Membership.JOIN) @@ -107,23 +131,23 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp }.disposeOnClear() } - private fun observeSelectionState() { - selectSubscribe(SpaceListViewState::selectedSpace) { spaceSummary -> - if (spaceSummary != null) { - // We only want to open group if the updated selectedGroup is a different one. - if (currentGroupId != spaceSummary.roomId) { - currentGroupId = spaceSummary.roomId - _viewEvents.post(SpaceListViewEvents.OpenSpace) - } - appStateHandler.setCurrentSpace(spaceSummary) - } else { - // If selected group is null we force to default. It can happens when leaving the selected group. - setState { - copy(selectedSpace = this.asyncSpaces()?.find { it.roomId == ALL_COMMUNITIES_GROUP_ID }) - } - } - } - } +// private fun observeSelectionState() { +// selectSubscribe(SpaceListViewState::selectedSpace) { spaceSummary -> +// if (spaceSummary != null) { +// // We only want to open group if the updated selectedGroup is a different one. +// if (currentGroupId != spaceSummary.roomId) { +// currentGroupId = spaceSummary.roomId +// _viewEvents.post(SpaceListViewEvents.OpenSpace) +// } +// appStateHandler.setCurrentSpace(spaceSummary.roomId) +// } else { +// // If selected group is null we force to default. It can happens when leaving the selected group. +// setState { +// copy(selectedSpace = this.asyncSpaces()?.find { it.roomId == ALL_COMMUNITIES_GROUP_ID }) +// } +// } +// } +// } override fun handle(action: SpaceListAction) { when (action) { @@ -132,15 +156,27 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp SpaceListAction.AddSpace -> handleAddSpace() is SpaceListAction.ToggleExpand -> handleToggleExpand(action) is SpaceListAction.OpenSpaceInvite -> handleSelectSpaceInvite(action) + is SpaceListAction.SelectLegacyGroup -> handleSelectGroup(action) } } // PRIVATE METHODS ***************************************************************************** private fun handleSelectSpace(action: SpaceListAction.SelectSpace) = withState { state -> - if (state.selectedSpace?.roomId != action.spaceSummary.roomId) { - setState { copy(selectedSpace = action.spaceSummary) } - uiStateRepository.storeSelectedSpace(action.spaceSummary.roomId, session.sessionId) + val groupingMethod = state.selectedGroupingMethod + if (groupingMethod is RoomGroupingMethod.ByLegacyGroup || groupingMethod.space()?.roomId != action.spaceSummary?.roomId) { + setState { copy(selectedGroupingMethod = RoomGroupingMethod.BySpace(action.spaceSummary)) } + appStateHandler.setCurrentSpace(action.spaceSummary?.roomId) + _viewEvents.post(SpaceListViewEvents.OpenSpace(groupingMethod is RoomGroupingMethod.ByLegacyGroup)) + } + } + + private fun handleSelectGroup(action: SpaceListAction.SelectLegacyGroup) = withState { state -> + val groupingMethod = state.selectedGroupingMethod + if (groupingMethod is RoomGroupingMethod.BySpace || groupingMethod.group()?.groupId != action.groupSummary?.groupId) { + setState { copy(selectedGroupingMethod = RoomGroupingMethod.ByLegacyGroup(action.groupSummary)) } + appStateHandler.setCurrentGroup(action.groupSummary?.groupId) + _viewEvents.post(SpaceListViewEvents.OpenGroup(groupingMethod is RoomGroupingMethod.BySpace)) } } @@ -176,38 +212,23 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp excludeType = listOf(/**RoomType.MESSAGING,$*/ null) } - Observable.combineLatest, List>( + Observable.combineLatest, List>( session .rx() .liveUser(session.myUserId) - .map { optionalUser -> - RoomSummary( - roomId = ALL_COMMUNITIES_GROUP_ID, - membership = Membership.JOIN, - displayName = stringProvider.getString(R.string.group_all_communities), - avatarUrl = optionalUser.getOrNull()?.avatarUrl ?: "", - encryptionEventTs = 0, - isEncrypted = false, - typingUsers = emptyList() - ) + .map { + it.getOrNull() }, session .rx() .liveSpaceSummaries(spaceSummaryQueryParams), - BiFunction { allCommunityGroup, communityGroups -> - (listOf(allCommunityGroup) + communityGroups) + BiFunction { _, communityGroups -> + communityGroups } ) .execute { async -> - val currentSelectedGroupId = selectedSpace?.roomId - val newSelectedGroup = if (currentSelectedGroupId != null) { - async()?.find { it.roomId == currentSelectedGroupId } - } else { - async()?.firstOrNull() - } copy( asyncSpaces = async, - selectedSpace = newSelectedGroup, rootSpaces = session.spaceService().getRootSpaceSummaries() ) } diff --git a/vector/src/main/java/im/vector/app/features/ui/SharedPreferencesUiStateRepository.kt b/vector/src/main/java/im/vector/app/features/ui/SharedPreferencesUiStateRepository.kt index 696a8c69cb7..6c6f0908c38 100644 --- a/vector/src/main/java/im/vector/app/features/ui/SharedPreferencesUiStateRepository.kt +++ b/vector/src/main/java/im/vector/app/features/ui/SharedPreferencesUiStateRepository.kt @@ -60,15 +60,35 @@ class SharedPreferencesUiStateRepository @Inject constructor( } override fun storeSelectedSpace(spaceId: String?, sessionId: String) { - sharedPreferences.edit { + sharedPreferences.edit(true) { putString("$KEY_SELECTED_SPACE@$sessionId", spaceId) } } + override fun storeSelectedGroup(groupId: String?, sessionId: String) { + sharedPreferences.edit(true) { + putString("$KEY_SELECTED_GROUP@$sessionId", groupId) + } + } + + override fun storeGroupingMethod(isSpace: Boolean, sessionId: String) { + sharedPreferences.edit(true) { + putBoolean("$KEY_SELECTED_METHOD@$sessionId", isSpace) + } + } + + override fun getSelectedGroup(sessionId: String): String? { + return sharedPreferences.getString("$KEY_SELECTED_GROUP@$sessionId", null) + } + override fun getSelectedSpace(sessionId: String): String? { return sharedPreferences.getString("$KEY_SELECTED_SPACE@$sessionId", null) } + override fun isGroupingMethodSpace(sessionId: String): Boolean { + return sharedPreferences.getBoolean("$KEY_SELECTED_METHOD@$sessionId", true) + } + companion object { private const val KEY_DISPLAY_MODE = "UI_STATE_DISPLAY_MODE" private const val VALUE_DISPLAY_MODE_CATCHUP = 0 @@ -76,5 +96,7 @@ class SharedPreferencesUiStateRepository @Inject constructor( private const val VALUE_DISPLAY_MODE_ROOMS = 2 private const val KEY_SELECTED_SPACE = "UI_STATE_SELECTED_SPACE" + private const val KEY_SELECTED_GROUP = "UI_STATE_SELECTED_GROUP" + private const val KEY_SELECTED_METHOD = "UI_STATE_SELECTED_METHOD" } } diff --git a/vector/src/main/java/im/vector/app/features/ui/UiStateRepository.kt b/vector/src/main/java/im/vector/app/features/ui/UiStateRepository.kt index 25e81efa83d..935da83f5de 100644 --- a/vector/src/main/java/im/vector/app/features/ui/UiStateRepository.kt +++ b/vector/src/main/java/im/vector/app/features/ui/UiStateRepository.kt @@ -33,6 +33,11 @@ interface UiStateRepository { fun storeDisplayMode(displayMode: RoomListDisplayMode) fun storeSelectedSpace(spaceId: String?, sessionId: String) + fun storeSelectedGroup(groupId: String?, sessionId: String) + + fun storeGroupingMethod(isSpace: Boolean, sessionId: String) fun getSelectedSpace(sessionId: String): String? + fun getSelectedGroup(sessionId: String): String? + fun isGroupingMethodSpace(sessionId: String): Boolean } diff --git a/vector/src/main/res/drawable/rounded_rect_stroke_8.xml b/vector/src/main/res/drawable/rounded_rect_stroke_8.xml new file mode 100644 index 00000000000..56696f1b58f --- /dev/null +++ b/vector/src/main/res/drawable/rounded_rect_stroke_8.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_space.xml b/vector/src/main/res/layout/item_space.xml index ef147f7730e..91952391be0 100644 --- a/vector/src/main/res/layout/item_space.xml +++ b/vector/src/main/res/layout/item_space.xml @@ -39,20 +39,20 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" - android:paddingStart="4dp" - android:paddingEnd="4dp" android:gravity="center" android:minWidth="16dp" android:minHeight="16dp" + android:paddingStart="4dp" + android:paddingEnd="4dp" android:textColor="@android:color/white" android:textSize="10sp" + android:visibility="gone" app:layout_constraintCircle="@+id/groupAvatarImageView" app:layout_constraintCircleAngle="45" app:layout_constraintCircleRadius="24dp" - android:visibility="gone" - tools:visibility="visible" tools:background="@drawable/bg_unread_highlight" - tools:text="147" /> + tools:text="147" + tools:visibility="visible" /> + + diff --git a/vector/src/main/res/layout/item_space_add.xml b/vector/src/main/res/layout/item_space_add.xml new file mode 100644 index 00000000000..12f75c0356c --- /dev/null +++ b/vector/src/main/res/layout/item_space_add.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_space_beta_header.xml b/vector/src/main/res/layout/item_space_beta_header.xml new file mode 100644 index 00000000000..77e260d9fd9 --- /dev/null +++ b/vector/src/main/res/layout/item_space_beta_header.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/view_state.xml b/vector/src/main/res/layout/view_state.xml index 731f3bd5c8a..51a7afe652b 100644 --- a/vector/src/main/res/layout/view_state.xml +++ b/vector/src/main/res/layout/view_state.xml @@ -9,6 +9,7 @@ diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 55e898264d8..0bbf2935874 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2198,9 +2198,6 @@ Enable swipe to reply in timeline Add a dedicated tab for unread notifications on main screen. - Spaces prototype - Spaces are the new way to group people and rooms for work, fun or just yourself. You can try them out now on Android, or from your computer. - Link copied to clipboard Add by matrix ID @@ -3337,4 +3334,7 @@ Add existing rooms and space + Welcome to Spaces! + Spaces are ways to group rooms and people for work, fun or just yourself. + You are invited diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml index aa55a3c5ec6..fef5a2fe9db 100644 --- a/vector/src/main/res/xml/vector_settings_labs.xml +++ b/vector/src/main/res/xml/vector_settings_labs.xml @@ -45,11 +45,4 @@ android:title="@string/labs_show_unread_notifications_as_tab" /> - - - \ No newline at end of file