From 7465ac2ef6fa09086e11332c152cb6bf6d619a9a Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 26 Apr 2021 17:34:58 +0200 Subject: [PATCH 1/4] Add just me space flow Initial commit --- .../vector/app/features/home/HomeActivity.kt | 17 +++++++++-- .../features/navigation/DefaultNavigator.kt | 29 ++++++++++++------- .../app/features/navigation/Navigator.kt | 8 ++++- .../features/spaces/SpaceCreationActivity.kt | 11 ++++++- .../create/ChoosePrivateSpaceTypeFragment.kt | 3 +- .../spaces/create/CreateSpaceEvents.kt | 2 +- .../spaces/create/CreateSpaceViewModel.kt | 20 +++++++++++-- 7 files changed, 68 insertions(+), 22 deletions(-) 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 5a7f2e2f8f..f01c27c30e 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 @@ -48,6 +48,7 @@ import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs import im.vector.app.features.disclaimer.showDisclaimerDialog import im.vector.app.features.matrixto.MatrixToBottomSheet +import im.vector.app.features.navigation.Navigator import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.permalink.NavigationInterceptor import im.vector.app.features.permalink.PermalinkHandler @@ -115,11 +116,21 @@ class HomeActivity : if (activityResult.resultCode == Activity.RESULT_OK) { val spaceId = SpaceCreationActivity.getCreatedSpaceId(activityResult.data) val defaultRoomId = SpaceCreationActivity.getDefaultRoomId(activityResult.data) + val isJustMe = SpaceCreationActivity.isJustMeSpace(activityResult.data) views.drawerLayout.closeDrawer(GravityCompat.START) + val postSwitchOption: Navigator.PostSwitchSpaceAction = if (defaultRoomId != null) { + Navigator.PostSwitchSpaceAction.OpenDefaultRoom(defaultRoomId, !isJustMe) + } else if (isJustMe) { + Navigator.PostSwitchSpaceAction.OpenAddExistingRooms + } else { + Navigator.PostSwitchSpaceAction.None + } // Here we want to change current space to the newly created one, and then immediately open the default room if (spaceId != null) { - navigator.switchToSpace(this, spaceId, defaultRoomId, true) + navigator.switchToSpace(context = this, + spaceId = spaceId, + postSwitchOption) } } } @@ -267,7 +278,7 @@ class HomeActivity : private fun renderState(state: HomeActivityViewState) { when (val status = state.initialSyncProgressServiceStatus) { - is InitialSyncProgressService.Status.Idle -> { + is InitialSyncProgressService.Status.Idle -> { views.waitingView.root.isVisible = false } is InitialSyncProgressService.Status.Progressing -> { @@ -494,7 +505,7 @@ class HomeActivity : } override fun switchToSpace(spaceId: String) { - navigator.switchToSpace(this@HomeActivity, spaceId, null, false) + navigator.switchToSpace(this@HomeActivity, spaceId, Navigator.PostSwitchSpaceAction.None) } } 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 73d8325bca..4ada274d77 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 @@ -74,6 +74,7 @@ import im.vector.app.features.share.SharedData import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet import im.vector.app.features.spaces.SpaceExploreActivity import im.vector.app.features.spaces.SpacePreviewActivity +import im.vector.app.features.spaces.manage.SpaceManageActivity import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.widgets.WidgetActivity import im.vector.app.features.widgets.WidgetArgsBuilder @@ -106,21 +107,27 @@ class DefaultNavigator @Inject constructor( startActivity(context, intent, buildTask) } - override fun switchToSpace(context: Context, spaceId: String, roomId: String?, openShareSheet: Boolean) { + override fun switchToSpace(context: Context, spaceId: String, postSwitchSpaceAction: Navigator.PostSwitchSpaceAction) { if (sessionHolder.getSafeActiveSession()?.getRoomSummary(spaceId) == null) { fatalError("Trying to open an unknown space $spaceId", vectorPreferences.failFast()) return } appStateHandler.setCurrentSpace(spaceId) - if (roomId != null) { - val args = RoomDetailArgs(roomId, eventId = null, openShareSpaceForId = spaceId.takeIf { openShareSheet }) - val intent = RoomDetailActivity.newIntent(context, args) - startActivity(context, intent, false) - } else { - // go back to home if we are showing room details? - // This is a bit ugly, but the navigator is supposed to know about the activity stack - if (context is RoomDetailActivity) { - context.finish() + when (postSwitchSpaceAction) { + Navigator.PostSwitchSpaceAction.None -> { + // go back to home if we are showing room details? + // This is a bit ugly, but the navigator is supposed to know about the activity stack + if (context is RoomDetailActivity) { + context.finish() + } + } + Navigator.PostSwitchSpaceAction.OpenAddExistingRooms -> { + startActivity(context, SpaceManageActivity.newIntent(context, spaceId), false) + } + is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> { + val args = RoomDetailArgs(postSwitchSpaceAction.roomId, eventId = null, openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet }) + val intent = RoomDetailActivity.newIntent(context, args) + startActivity(context, intent, false) } } } @@ -236,7 +243,7 @@ class DefaultNavigator @Inject constructor( } override fun switchToSpace(spaceId: String) { - this@DefaultNavigator.switchToSpace(context, spaceId, null, openShareSheet = false) + this@DefaultNavigator.switchToSpace(context, spaceId, Navigator.PostSwitchSpaceAction.None) } } // TODO check if there is already one?? diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 489cd37987..2302a749e7 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -38,7 +38,13 @@ interface Navigator { fun openRoom(context: Context, roomId: String, eventId: String? = null, buildTask: Boolean = false) - fun switchToSpace(context: Context, spaceId: String, roomId: String?, openShareSheet: Boolean) + sealed class PostSwitchSpaceAction { + object None : PostSwitchSpaceAction() + data class OpenDefaultRoom(val roomId: String, val showShareSheet: Boolean) : PostSwitchSpaceAction() + object OpenAddExistingRooms: PostSwitchSpaceAction() + } + + fun switchToSpace(context: Context, spaceId: String, postSwitchSpaceAction: PostSwitchSpaceAction) fun openSpacePreview(context: Context, spaceId: String) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt index 3e48a3dee3..21617a1259 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt @@ -36,6 +36,7 @@ import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment import im.vector.app.features.spaces.create.CreateSpaceEvents import im.vector.app.features.spaces.create.CreateSpaceState import im.vector.app.features.spaces.create.CreateSpaceViewModel +import im.vector.app.features.spaces.create.SpaceTopology import im.vector.app.features.spaces.create.SpaceType import javax.inject.Inject @@ -61,7 +62,7 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac CreateSpaceState.Step.SetDetails -> { navigateToFragment(ChooseSpaceTypeFragment::class.java) } - CreateSpaceState.Step.AddRooms -> { + CreateSpaceState.Step.AddRooms -> { navigateToFragment(CreateSpaceDefaultRoomsFragment::class.java) } CreateSpaceState.Step.ChoosePrivateType -> { @@ -106,6 +107,9 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac setResult(RESULT_OK, Intent().apply { putExtra(RESULT_DATA_CREATED_SPACE_ID, it.spaceId) putExtra(RESULT_DATA_DEFAULT_ROOM_ID, it.defaultRoomId) + if (it.topology == SpaceTopology.JustMe) { + putExtra(RESULT_DATA_CREATED_SPACE_IS_JUST_ME, true) + } }) finish() } @@ -155,6 +159,7 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac companion object { private const val RESULT_DATA_CREATED_SPACE_ID = "RESULT_DATA_CREATED_SPACE_ID" private const val RESULT_DATA_DEFAULT_ROOM_ID = "RESULT_DATA_DEFAULT_ROOM_ID" + private const val RESULT_DATA_CREATED_SPACE_IS_JUST_ME = "RESULT_DATA_CREATED_SPACE_IS_JUST_ME" fun newIntent(context: Context): Intent { return Intent(context, SpaceCreationActivity::class.java).apply { @@ -169,6 +174,10 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac fun getDefaultRoomId(data: Intent?): String? { return data?.extras?.getString(RESULT_DATA_DEFAULT_ROOM_ID) } + + fun isJustMeSpace(data: Intent?): Boolean { + return data?.extras?.getBoolean(RESULT_DATA_CREATED_SPACE_IS_JUST_ME, false) == true + } } override fun create(initialState: CreateSpaceState): CreateSpaceViewModel = viewModelFactory.create(initialState) diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt index 26661b623f..196ec98d6b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt @@ -42,8 +42,7 @@ class ChoosePrivateSpaceTypeFragment @Inject constructor( super.onViewCreated(view, savedInstanceState) views.justMeButton.setOnClickListener(DebouncedClickListener({ - vectorBaseActivity.notImplemented("Organize room as space is not yet implemented") -// sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.JustMe)) + sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.JustMe)) })) views.teammatesButton.setOnClickListener(DebouncedClickListener({ diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt index 0023399612..c3fa2b2068 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt @@ -24,7 +24,7 @@ sealed class CreateSpaceEvents : VectorViewEvents { object NavigateToAddRooms : CreateSpaceEvents() object NavigateToChoosePrivateType : CreateSpaceEvents() object Dismiss : CreateSpaceEvents() - data class FinishSuccess(val spaceId: String, val defaultRoomId: String?) : CreateSpaceEvents() + data class FinishSuccess(val spaceId: String, val defaultRoomId: String?, val topology: SpaceTopology?) : CreateSpaceEvents() data class ShowModalError(val errorMessage: String) : CreateSpaceEvents() object HideModalLoading : CreateSpaceEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index e61707e6a2..ef2f5029e7 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -126,10 +126,12 @@ class CreateSpaceViewModel @AssistedInject constructor( SpaceTopology.JustMe -> { setState { copy( - spaceTopology = SpaceTopology.JustMe + spaceTopology = SpaceTopology.JustMe, + defaultRooms = emptyMap() ) } // XXX finish and open the add rooms directly + handleNextFromDefaultRooms() } SpaceTopology.MeAndTeammates -> { setState { @@ -237,14 +239,26 @@ class CreateSpaceViewModel @AssistedInject constructor( setState { copy(creationResult = Success(result.spaceId)) } - _viewEvents.post(CreateSpaceEvents.FinishSuccess(result.spaceId, result.childIds.firstOrNull())) + _viewEvents.post( + CreateSpaceEvents.FinishSuccess( + result.spaceId, + result.childIds.firstOrNull(), + state.spaceTopology + ) + ) } is CreateSpaceTaskResult.PartialSuccess -> { // XXX what can we do here? setState { copy(creationResult = Success(result.spaceId)) } - _viewEvents.post(CreateSpaceEvents.FinishSuccess(result.spaceId, result.childIds.firstOrNull())) + _viewEvents.post( + CreateSpaceEvents.FinishSuccess( + result.spaceId, + result.childIds.firstOrNull(), + state.spaceTopology + ) + ) } is CreateSpaceTaskResult.FailedToCreateSpace -> { setState { From 31ffa65fd068e5c5217f41b22ebef116368f9055 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 27 Apr 2021 14:15:52 +0200 Subject: [PATCH 2/4] Update add room screen as per design --- .../session/room/UpdatableLivePageResult.kt | 8 ++ .../room/summary/RoomSummaryDataSource.kt | 23 ++++- .../spaces/manage/AddRoomListController.kt | 46 ++++++++-- .../spaces/manage/SpaceAddRoomFragment.kt | 85 +++++++++++++++++-- .../spaces/manage/SpaceAddRoomsViewModel.kt | 28 +++++- 5 files changed, 174 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableLivePageResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableLivePageResult.kt index 3bcca0c241..b83f57f5ef 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableLivePageResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableLivePageResult.kt @@ -24,4 +24,12 @@ interface UpdatableLivePageResult { val livePagedList: LiveData> fun updateQuery(builder: (RoomSummaryQueryParams) -> RoomSummaryQueryParams) + + val liveBoundaries: LiveData } + +data class ResultBoundaries( + val frontLoaded: Boolean = false, + val endLoaded: Boolean = false, + val zeroItemLoaded: Boolean = false +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt index d2bb51cbef..887219fdd8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.summary import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.paging.LivePagedListBuilder import androidx.paging.PagedList @@ -28,6 +29,7 @@ import io.realm.Sort import io.realm.kotlin.where import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter +import org.matrix.android.sdk.api.session.room.ResultBoundaries import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult @@ -187,9 +189,25 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat roomSummaryMapper.map(it) } + val boundaries = MutableLiveData(ResultBoundaries()) + val mapped = monarchy.findAllPagedWithChanges( realmDataSourceFactory, - LivePagedListBuilder(dataSourceFactory, pagedListConfig) + LivePagedListBuilder(dataSourceFactory, pagedListConfig).also { + it.setBoundaryCallback(object : PagedList.BoundaryCallback() { + override fun onItemAtEndLoaded(itemAtEnd: RoomSummary) { + boundaries.postValue(boundaries.value?.copy(frontLoaded = true)) + } + + override fun onItemAtFrontLoaded(itemAtFront: RoomSummary) { + boundaries.postValue(boundaries.value?.copy(endLoaded = true)) + } + + override fun onZeroItemsLoaded() { + boundaries.postValue(boundaries.value?.copy(zeroItemLoaded = true)) + } + }) + } ) return object : UpdatableLivePageResult { @@ -200,6 +218,9 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat roomSummariesQuery(it, builder.invoke(queryParams)).process(sortOrder) } } + + override val liveBoundaries: LiveData + get() = boundaries } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt index cc6ebe0f60..46ca2a6606 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt @@ -22,6 +22,8 @@ import com.airbnb.epoxy.paging.PagedListEpoxyController import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.core.utils.createUIHandler import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.list.RoomCategoryItem_ +import org.matrix.android.sdk.api.session.room.ResultBoundaries import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.util.toMatrixItem @@ -54,6 +56,26 @@ class AddRoomListController @Inject constructor( var listener: Listener? = null var ignoreRooms: List? = null + var initialLoadOccurred = false + + fun boundaryChange(boundary: ResultBoundaries) { + val boundaryHasLoadedSomething = boundary.frontLoaded || boundary.zeroItemLoaded + if (initialLoadOccurred != boundaryHasLoadedSomething) { + initialLoadOccurred = boundaryHasLoadedSomething + requestForcedModelBuild() + } + } + + var sectionName: String? = null + set(value) { + if (value != field) { + field = value + requestForcedModelBuild() + } + } + + var totalSize: Int = 0 + var selectedItems: Map = emptyMap() set(value) { field = value @@ -62,15 +84,29 @@ class AddRoomListController @Inject constructor( } override fun addModels(models: List>) { - if (ignoreRooms == null) { - super.addModels(models) + val filteredModel = if (ignoreRooms == null) { + models } else { - super.addModels( - models.filter { - it !is RoomSelectionItem || !ignoreRooms!!.contains(it.matrixItem.id) + models.filter { + it !is RoomSelectionItem || !ignoreRooms!!.contains(it.matrixItem.id) + } + } + val somethingToShow = filteredModel.isNotEmpty() || !initialLoadOccurred + if (somethingToShow || filteredModel.isNotEmpty()) { + add( + RoomCategoryItem_().apply { + id("header") + title(sectionName ?: "") + expanded(true) } ) } + super.addModels(filteredModel) + if (!initialLoadOccurred) { + add( + RoomSelectionPlaceHolderItem_().apply { id("loading") } + ) + } } override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt index dc55373b36..2143b31be7 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt @@ -16,6 +16,9 @@ package im.vector.app.features.spaces.manage +import android.graphics.Canvas +import android.graphics.Rect +import android.graphics.drawable.Drawable import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -23,23 +26,30 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.ConcatAdapter +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.airbnb.epoxy.EpoxyViewHolder import com.airbnb.mvrx.Loading import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.fragmentViewModel import com.jakewharton.rxbinding3.appcompat.queryTextChanges import im.vector.app.R import im.vector.app.core.extensions.cleanup -import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentSpaceAddRoomsBinding +import im.vector.app.features.home.room.list.RoomCategoryItem_ import io.reactivex.rxkotlin.subscribeBy import org.matrix.android.sdk.api.session.room.model.RoomSummary import java.util.concurrent.TimeUnit import javax.inject.Inject class SpaceAddRoomFragment @Inject constructor( - private val epoxyController: AddRoomListController, + private val spaceEpoxyController: AddRoomListController, + private val roomEpoxyController: AddRoomListController, private val viewModelFactory: SpaceAddRoomsViewModel.Factory ) : VectorBaseFragment(), OnBackPressed, AddRoomListController.Listener, SpaceAddRoomsViewModel.Factory { @@ -79,7 +89,8 @@ class SpaceAddRoomFragment @Inject constructor( .disposeOnDestroyView() viewModel.selectionListLiveData.observe(viewLifecycleOwner) { - epoxyController.selectedItems = it + spaceEpoxyController.selectedItems = it + roomEpoxyController.selectedItems = it saveNeeded = it.values.any { it } invalidateOptionsMenu() } @@ -89,7 +100,8 @@ class SpaceAddRoomFragment @Inject constructor( }.disposeOnDestroyView() viewModel.selectSubscribe(this, SpaceAddRoomsState::ignoreRooms) { - epoxyController.ignoreRooms = it + spaceEpoxyController.ignoreRooms = it + roomEpoxyController.ignoreRooms = it }.disposeOnDestroyView() viewModel.selectSubscribe(this, SpaceAddRoomsState::isSaving) { @@ -142,16 +154,71 @@ class SpaceAddRoomFragment @Inject constructor( override fun onDestroyView() { views.roomList.cleanup() - epoxyController.listener = null + spaceEpoxyController.listener = null + roomEpoxyController.listener = null super.onDestroyView() } private fun setupRecyclerView() { - views.roomList.configureWith(epoxyController, showDivider = true) - epoxyController.listener = this - viewModel.updatableLivePageResult.livePagedList.observe(viewLifecycleOwner) { - epoxyController.submitList(it) + val concatAdapter = ConcatAdapter() + spaceEpoxyController.sectionName = getString(R.string.spaces_header) + roomEpoxyController.sectionName = getString(R.string.rooms_header) + spaceEpoxyController.listener = this + roomEpoxyController.listener = this + + viewModel.updatableLiveSpacePageResult.liveBoundaries.observe(viewLifecycleOwner) { + spaceEpoxyController.boundaryChange(it) } + viewModel.updatableLiveSpacePageResult.livePagedList.observe(viewLifecycleOwner) { + spaceEpoxyController.totalSize = it.size + spaceEpoxyController.submitList(it) + } + + viewModel.updatableLivePageResult.liveBoundaries.observe(viewLifecycleOwner) { + roomEpoxyController.boundaryChange(it) + } + viewModel.updatableLivePageResult.livePagedList.observe(viewLifecycleOwner) { + roomEpoxyController.totalSize = it.size + roomEpoxyController.submitList(it) + } + + views.roomList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + views.roomList.addItemDecoration( + + object : DividerItemDecoration(context, VERTICAL) { + val decorationDrawable = ContextCompat.getDrawable(requireContext(), R.drawable.divider_horizontal) + + override fun getDrawable(): Drawable? { + return decorationDrawable + } + + override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { + val position = parent.getChildAdapterPosition(view) + val vh = parent.findViewHolderForAdapterPosition(position) + val nextIsSectionOrFinal = parent.findViewHolderForAdapterPosition(position + 1)?.let { + (it as? EpoxyViewHolder)?.model is RoomCategoryItem_ + } ?: true + if (vh == null + || (vh as? EpoxyViewHolder)?.model is RoomCategoryItem_ + || nextIsSectionOrFinal + ) { + outRect.setEmpty() + } else { + super.getItemOffsets(outRect, view, parent, state) + } + } + + override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + super.onDraw(c, parent, state) + } + } + ) + views.roomList.setHasFixedSize(true) + + concatAdapter.addAdapter(roomEpoxyController.adapter) + concatAdapter.addAdapter(spaceEpoxyController.adapter) + + views.roomList.adapter = concatAdapter } override fun onBackPressed(toolbarButton: Boolean): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt index e12aa14e83..5b1674d44a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt @@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams class AddRoomError(val errorList: Map) : Throwable() { @@ -53,11 +54,31 @@ class SpaceAddRoomsViewModel @AssistedInject constructor( private val session: Session ) : VectorViewModel(initialState) { - val updatableLivePageResult: UpdatableLivePageResult by lazy { + val updatableLiveSpacePageResult: UpdatableLivePageResult by lazy { session.getFilteredPagedRoomSummariesLive( roomSummaryQueryParams { this.memberships = listOf(Membership.JOIN) this.excludeType = null + this.includeType = listOf(RoomType.SPACE) + this.activeSpaceId = ActiveSpaceFilter.ExcludeSpace(initialState.spaceId) + this.displayName = QueryStringValue.Contains(initialState.currentFilter, QueryStringValue.Case.INSENSITIVE) + }, + pagedListConfig = PagedList.Config.Builder() + .setPageSize(10) + .setInitialLoadSizeHint(20) + .setEnablePlaceholders(true) + .setPrefetchDistance(10) + .build(), + sortOrder = RoomSortOrder.NAME + ) + } + + val updatableLivePageResult: UpdatableLivePageResult by lazy { + session.getFilteredPagedRoomSummariesLive( + roomSummaryQueryParams { + this.memberships = listOf(Membership.JOIN) + this.excludeType = listOf(RoomType.SPACE) + this.includeType = null this.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS this.activeSpaceId = ActiveSpaceFilter.ExcludeSpace(initialState.spaceId) this.displayName = QueryStringValue.Contains(initialState.currentFilter, QueryStringValue.Case.INSENSITIVE) @@ -116,6 +137,11 @@ class SpaceAddRoomsViewModel @AssistedInject constructor( displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE) ) } + updatableLiveSpacePageResult.updateQuery { + it.copy( + displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE) + ) + } setState { copy( currentFilter = action.filter From f89a7496aa4acdcc08cfc40ce1a9c6fc6b5f1942 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 28 Apr 2021 14:52:15 +0200 Subject: [PATCH 3/4] Add action to create new room --- .../features/navigation/DefaultNavigator.kt | 6 +++- .../createroom/CreateRoomFragment.kt | 3 +- .../createroom/CreateRoomViewModel.kt | 22 +++++++++++--- .../createroom/CreateRoomViewState.kt | 6 ++-- .../spaces/SpaceSettingsMenuBottomSheet.kt | 3 +- .../spaces/manage/SpaceAddRoomFragment.kt | 6 ++-- .../spaces/manage/SpaceManageActivity.kt | 26 +++++++++++++++- .../manage/SpaceManageSharedViewModel.kt | 1 + .../spaces/manage/SpaceManagedSharedAction.kt | 1 + .../manage/SpaceManagedSharedViewEvents.kt | 1 + .../res/layout/fragment_space_add_rooms.xml | 30 +++++++++---------- 11 files changed, 77 insertions(+), 28 deletions(-) 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 4ada274d77..735a29afa4 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 @@ -125,7 +125,11 @@ class DefaultNavigator @Inject constructor( startActivity(context, SpaceManageActivity.newIntent(context, spaceId), false) } is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> { - val args = RoomDetailArgs(postSwitchSpaceAction.roomId, eventId = null, openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet }) + val args = RoomDetailArgs( + postSwitchSpaceAction.roomId, + eventId = null, + openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet } + ) val intent = RoomDetailActivity.newIntent(context, args) startActivity(context, intent, false) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt index 5a9e3a13aa..d7b4cb4dad 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -47,7 +47,8 @@ import javax.inject.Inject @Parcelize data class CreateRoomArgs( - val initialName: String + val initialName: String, + val parentSpaceId: String? = null ) : Parcelable class CreateRoomFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt index a86f06b142..952f08c380 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt @@ -42,8 +42,9 @@ import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset +import timber.log.Timber -class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState, +class CreateRoomViewModel @AssistedInject constructor(@Assisted val initialState: CreateRoomViewState, private val session: Session, private val rawService: RawService ) : VectorViewModel(initialState) { @@ -133,7 +134,8 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr CreateRoomViewState( isEncrypted = adminE2EByDefault, - hsAdminHasDisabledE2E = !adminE2EByDefault + hsAdminHasDisabledE2E = !adminE2EByDefault, + parentSpaceId = initialState.parentSpaceId ) } @@ -228,9 +230,21 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr // TODO: Should this be non-cancellable? viewModelScope.launch { - val result = runCatching { session.createRoom(createRoomParams) } - result.fold( + runCatching { session.createRoom(createRoomParams) }.fold( { roomId -> + + if (initialState.parentSpaceId != null) { + // add it as a child + try { + val via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList() + session.spaceService() + .getSpace(initialState.parentSpaceId) + ?.addChildren(roomId, viaServers = via, order = null) + } catch (failure: Throwable) { + Timber.w(failure, "Failed to add as a child") + } + } + setState { copy(asyncCreateRoomRequest = Success(roomId)) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt index 6bc19dfa20..f060e998ad 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt @@ -32,11 +32,13 @@ data class CreateRoomViewState( val disableFederation: Boolean = false, val homeServerName: String = "", val hsAdminHasDisabledE2E: Boolean = false, - val asyncCreateRoomRequest: Async = Uninitialized + val asyncCreateRoomRequest: Async = Uninitialized, + val parentSpaceId: String? ) : MvRxState { constructor(args: CreateRoomArgs) : this( - roomName = args.initialName + roomName = args.initialName, + parentSpaceId = args.parentSpaceId ) /** diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt index 82337a3600..5a1d80c240 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt @@ -117,7 +117,8 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment(), SpaceManageSharedViewModel.Factory { @Inject lateinit var sharedViewModelFactory: SpaceManageSharedViewModel.Factory + private lateinit var sharedDirectoryActionViewModel: RoomDirectorySharedActionViewModel override fun injectWith(injector: ScreenComponent) { injector.inject(this) @@ -53,9 +59,20 @@ class SpaceManageActivity : VectorBaseActivity(), SpaceMa override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + sharedDirectoryActionViewModel = viewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) + sharedDirectoryActionViewModel + .observe() + .subscribe { sharedAction -> + when (sharedAction) { + is RoomDirectorySharedAction.Back, + is RoomDirectorySharedAction.Close -> finish() + } + } + .disposeOnDestroy() + + val args = intent?.getParcelableExtra(MvRx.KEY_ARG) if (isFirstCreation()) { val simpleName = SpaceAddRoomFragment::class.java.simpleName - val args = intent?.getParcelableExtra(MvRx.KEY_ARG) if (supportFragmentManager.findFragmentByTag(simpleName) == null) { supportFragmentManager.commitTransaction { replace(R.id.simpleFragmentContainer, @@ -78,6 +95,13 @@ class SpaceManageActivity : VectorBaseActivity(), SpaceMa SpaceManagedSharedViewEvents.ShowLoading -> { views.simpleActivityWaitingView.isVisible = true } + SpaceManagedSharedViewEvents.NavigateToCreateRoom -> { + addFragmentToBackstack( + R.id.simpleFragmentContainer, + CreateRoomFragment::class.java, + CreateRoomArgs("", parentSpaceId = args?.spaceId) + ) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt index 1d8612f084..24110c081a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt @@ -54,6 +54,7 @@ class SpaceManageSharedViewModel @AssistedInject constructor( } SpaceManagedSharedAction.HideLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.HideLoading) SpaceManagedSharedAction.ShowLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.ShowLoading) + SpaceManagedSharedAction.CreateRoom -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToCreateRoom) } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt index 59e2742379..1c0de1041b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt @@ -22,4 +22,5 @@ sealed class SpaceManagedSharedAction : VectorViewModelAction { object HandleBack : SpaceManagedSharedAction() object ShowLoading : SpaceManagedSharedAction() object HideLoading : SpaceManagedSharedAction() + object CreateRoom : SpaceManagedSharedAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt index 067592b6ce..0cf6bcaee1 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt @@ -22,4 +22,5 @@ sealed class SpaceManagedSharedViewEvents : VectorViewEvents { object Finish : SpaceManagedSharedViewEvents() object ShowLoading : SpaceManagedSharedViewEvents() object HideLoading : SpaceManagedSharedViewEvents() + object NavigateToCreateRoom : SpaceManagedSharedViewEvents() } diff --git a/vector/src/main/res/layout/fragment_space_add_rooms.xml b/vector/src/main/res/layout/fragment_space_add_rooms.xml index 23b2807151..6619d06cca 100644 --- a/vector/src/main/res/layout/fragment_space_add_rooms.xml +++ b/vector/src/main/res/layout/fragment_space_add_rooms.xml @@ -82,21 +82,21 @@ app:layout_constraintTop_toBottomOf="@+id/addRoomToSpaceToolbar" app:queryHint="@string/search_hint_room_name" /> - - - - - - - - - - - - - - - + From 30d7c73b2dba086401589e7c14096bbe8ffc3939 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 29 Apr 2021 11:56:57 +0200 Subject: [PATCH 4/4] code review --- .../features/roomdirectory/createroom/CreateRoomViewModel.kt | 4 ++-- .../im/vector/app/features/spaces/SpaceCreationActivity.kt | 4 +--- .../vector/app/features/spaces/create/CreateSpaceViewModel.kt | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt index 952f08c380..4eb05fb697 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt @@ -44,7 +44,7 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import timber.log.Timber -class CreateRoomViewModel @AssistedInject constructor(@Assisted val initialState: CreateRoomViewState, +class CreateRoomViewModel @AssistedInject constructor(@Assisted private val initialState: CreateRoomViewState, private val session: Session, private val rawService: RawService ) : VectorViewModel(initialState) { @@ -236,7 +236,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted val initialState if (initialState.parentSpaceId != null) { // add it as a child try { - val via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList() + val via = session.sessionParams.homeServerHost?.let { listOf(it) }.orEmpty() session.spaceService() .getSpace(initialState.parentSpaceId) ?.addChildren(roomId, viaServers = via, order = null) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt index 21617a1259..bc06d1a52b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt @@ -107,9 +107,7 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac setResult(RESULT_OK, Intent().apply { putExtra(RESULT_DATA_CREATED_SPACE_ID, it.spaceId) putExtra(RESULT_DATA_DEFAULT_ROOM_ID, it.defaultRoomId) - if (it.topology == SpaceTopology.JustMe) { - putExtra(RESULT_DATA_CREATED_SPACE_IS_JUST_ME, true) - } + putExtra(RESULT_DATA_CREATED_SPACE_IS_JUST_ME, it.topology == SpaceTopology.JustMe) }) finish() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index ef2f5029e7..9881232f4d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -130,7 +130,6 @@ class CreateSpaceViewModel @AssistedInject constructor( defaultRooms = emptyMap() ) } - // XXX finish and open the add rooms directly handleNextFromDefaultRooms() } SpaceTopology.MeAndTeammates -> {