From 8146d8ab1e787be3d6ed28ab849e8f919b3168a3 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 16 Apr 2021 16:55:11 +0200 Subject: [PATCH] Add Rooms to space --- .../sdk/api/query/ActiveSpaceFilter.kt | 1 + .../sdk/api/session/room/RoomService.kt | 6 +- .../sdk/api/session/room/RoomSortOrder.kt | 23 +++ .../sdk/api/session/room/model/RoomSummary.kt | 3 +- .../database/mapper/RoomSummaryMapper.kt | 3 +- .../session/room/DefaultRoomService.kt | 9 +- .../room/summary/RoomSummaryDataSource.kt | 51 ++++- .../session/space/DefaultSpaceService.kt | 3 +- tools/check/forbidden_strings_in_code.txt | 2 +- vector/sampledata/matrix.json | 6 + vector/src/main/AndroidManifest.xml | 1 + .../im/vector/app/core/di/FragmentModule.kt | 6 + .../im/vector/app/core/di/ScreenComponent.kt | 2 + .../spaces/SpaceSettingsMenuBottomSheet.kt | 8 + .../spaces/manage/AddRoomListController.kt | 89 ++++++++ .../spaces/manage/RoomSelectionItem.kt | 67 ++++++ .../manage/RoomSelectionPlaceHolderItem.kt | 27 +++ .../spaces/manage/SpaceAddRoomActions.kt | 27 +++ .../spaces/manage/SpaceAddRoomFragment.kt | 171 +++++++++++++++ .../spaces/manage/SpaceAddRoomsState.kt | 35 ++++ .../spaces/manage/SpaceAddRoomsViewEvents.kt | 25 +++ .../spaces/manage/SpaceAddRoomsViewModel.kt | 194 ++++++++++++++++++ .../spaces/manage/SpaceManageActivity.kt | 94 +++++++++ .../manage/SpaceManageSharedViewModel.kt | 59 ++++++ .../spaces/manage/SpaceManageViewState.kt | 27 +++ .../spaces/manage/SpaceManagedSharedAction.kt | 25 +++ .../manage/SpaceManagedSharedViewEvents.kt | 25 +++ .../src/main/res/drawable/ic_checkbox_off.xml | 11 + .../src/main/res/drawable/ic_checkbox_on.xml | 18 ++ .../layout/bottom_sheet_space_settings.xml | 12 ++ .../res/layout/fragment_space_add_rooms.xml | 103 ++++++++++ .../res/layout/item_room_to_add_in_space.xml | 56 +++++ .../item_room_to_add_in_space_placeholder.xml | 56 +++++ .../src/main/res/menu/menu_space_add_room.xml | 9 + vector/src/main/res/values/strings.xml | 6 + 35 files changed, 1244 insertions(+), 16 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSortOrder.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionItem.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionPlaceHolderItem.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomActions.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewEvents.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt create mode 100644 vector/src/main/res/drawable/ic_checkbox_off.xml create mode 100644 vector/src/main/res/drawable/ic_checkbox_on.xml create mode 100644 vector/src/main/res/layout/fragment_space_add_rooms.xml create mode 100644 vector/src/main/res/layout/item_room_to_add_in_space.xml create mode 100644 vector/src/main/res/layout/item_room_to_add_in_space_placeholder.xml create mode 100644 vector/src/main/res/menu/menu_space_add_room.xml diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/ActiveSpaceFilter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/ActiveSpaceFilter.kt index 3c716fe0fe..48619b9394 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/ActiveSpaceFilter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/ActiveSpaceFilter.kt @@ -19,4 +19,5 @@ package org.matrix.android.sdk.api.query sealed class ActiveSpaceFilter { object None : ActiveSpaceFilter() data class ActiveSpace(val currentSpaceId: String?) : ActiveSpaceFilter() + data class ExcludeSpace(val spaceId: String) : ActiveSpaceFilter() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index 092c31de1d..871c5378a6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -178,13 +178,15 @@ interface RoomService { * TODO Doc */ fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, - pagedListConfig: PagedList.Config = defaultPagedListConfig): LiveData> + pagedListConfig: PagedList.Config = defaultPagedListConfig, + sortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY): LiveData> /** * TODO Doc */ fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, - pagedListConfig: PagedList.Config = defaultPagedListConfig): UpdatableLivePageResult + pagedListConfig: PagedList.Config = defaultPagedListConfig, + sortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY): UpdatableLivePageResult /** * TODO Doc diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSortOrder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSortOrder.kt new file mode 100644 index 0000000000..36da242527 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSortOrder.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.api.session.room + +enum class RoomSortOrder { + NAME, + ACTIVITY, + NONE +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt index f08f605a24..2a802adcde 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt @@ -57,7 +57,8 @@ data class RoomSummary constructor( val hasFailedSending: Boolean = false, val roomType: String? = null, val spaceParents: List? = null, - val children: List? = null + val children: List? = null, + val flattenParentIds: List = emptyList() ) { val isVersioned: Boolean diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt index 87ac48e107..9109ef2e8f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt @@ -89,7 +89,8 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa viaServers = it.viaServers.toList(), parentRoomId = roomSummaryEntity.roomId ) - } + }, + flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList() ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index a871c53eed..d9fe1288e2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -23,6 +23,7 @@ import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.RoomService +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 import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState @@ -91,14 +92,14 @@ internal class DefaultRoomService @Inject constructor( return roomSummaryDataSource.getRoomSummariesLive(queryParams) } - override fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config) + override fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config, sortOrder: RoomSortOrder) : LiveData> { - return roomSummaryDataSource.getSortedPagedRoomSummariesLive(queryParams, pagedListConfig) + return roomSummaryDataSource.getSortedPagedRoomSummariesLive(queryParams, pagedListConfig, sortOrder) } - override fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config) + override fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config, sortOrder: RoomSortOrder) : UpdatableLivePageResult { - return roomSummaryDataSource.getUpdatablePagedRoomSummariesLive(queryParams, pagedListConfig) + return roomSummaryDataSource.getUpdatablePagedRoomSummariesLive(queryParams, pagedListConfig, sortOrder) } override fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount { 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 ea4d2df473..06f84066be 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 @@ -28,6 +28,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.RoomSortOrder import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.model.Membership @@ -162,10 +163,22 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat } fun getSortedPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, - pagedListConfig: PagedList.Config): LiveData> { + pagedListConfig: PagedList.Config, + sortOrder: RoomSortOrder): LiveData> { val realmDataSourceFactory = monarchy.createDataSourceFactory { realm -> roomSummariesQuery(realm, queryParams) - .sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) + .apply { + when (sortOrder) { + RoomSortOrder.NAME -> { + sort(RoomSummaryEntityFields.DISPLAY_NAME, Sort.ASCENDING) + } + RoomSortOrder.ACTIVITY -> { + sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) + } + RoomSortOrder.NONE -> { + } + } + } } val dataSourceFactory = realmDataSourceFactory.map { roomSummaryMapper.map(it) @@ -177,10 +190,22 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat } fun getUpdatablePagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, - pagedListConfig: PagedList.Config): UpdatableLivePageResult { + pagedListConfig: PagedList.Config, + sortOrder: RoomSortOrder): UpdatableLivePageResult { val realmDataSourceFactory = monarchy.createDataSourceFactory { realm -> roomSummariesQuery(realm, queryParams) - .sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) + .apply { + when (sortOrder) { + RoomSortOrder.NAME -> { + sort(RoomSummaryEntityFields.DISPLAY_NAME, Sort.ASCENDING) + } + RoomSortOrder.ACTIVITY -> { + sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) + } + RoomSortOrder.NONE -> { + } + } + } } val dataSourceFactory = realmDataSourceFactory.map { roomSummaryMapper.map(it) @@ -197,7 +222,18 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat override fun updateQuery(builder: (RoomSummaryQueryParams) -> RoomSummaryQueryParams) { realmDataSourceFactory.updateQuery { roomSummariesQuery(it, builder.invoke(queryParams)) - .sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) + .apply { + when (sortOrder) { + RoomSortOrder.NAME -> { + sort(RoomSummaryEntityFields.DISPLAY_NAME, Sort.ASCENDING) + } + RoomSortOrder.ACTIVITY -> { + sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) + } + RoomSortOrder.NONE -> { + } + } + } } } } @@ -272,7 +308,10 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat query.contains(RoomSummaryEntityFields.FLATTEN_PARENT_IDS, queryParams.activeSpaceId.currentSpaceId) } } - else -> { + is ActiveSpaceFilter.ExcludeSpace -> { + query.not().contains(RoomSummaryEntityFields.FLATTEN_PARENT_IDS, queryParams.activeSpaceId.spaceId) + } + else -> { // nop } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index ba063df375..9ae8b25e5d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -120,7 +120,8 @@ internal class DefaultSpaceService @Inject constructor( avatarUrl = spaceDesc?.avatarUrl ?: "", encryptionEventTs = null, typingUsers = emptyList(), - isEncrypted = false + isEncrypted = false, + flattenParentIds = emptyList() ), second = response.rooms ?.filter { it.roomId != spaceId } diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index 8695dafb17..8f9cc852a7 100644 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -161,7 +161,7 @@ Formatter\.formatShortFileSize===1 # android\.text\.TextUtils ### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt -enum class===98 +enum class===99 ### Do not import temporary legacy classes import org.matrix.android.sdk.internal.legacy.riot===3 diff --git a/vector/sampledata/matrix.json b/vector/sampledata/matrix.json index 5328ec81b7..c69e0201ad 100644 --- a/vector/sampledata/matrix.json +++ b/vector/sampledata/matrix.json @@ -6,6 +6,7 @@ "message": "William Shakespeare (bapt. 26 April 1564 – 23 April 1616) was an English poet, playwright and actor, widely regarded as the greatest writer in the English language and the world's greatest dramatist. He is often called England's national poet and the \"Bard of Avon\". His extant works, including collaborations, consist of approximately 39 plays, 154 sonnets, two long narrative poems, and a few other verses, some of uncertain authorship. His plays have been translated into every major living language and are performed more often than those of any other playwright.\n\nShakespeare was born and raised in Stratford-upon-Avon, Warwickshire. At the age of 18, he married Anne Hathaway, with whom he had three children: Susanna and twins Hamnet and Judith. Sometime between 1585 and 1592, he began a successful career in London as an actor, writer, and part-owner of a playing company called the Lord Chamberlain's Men, later known as the King's Men. At age 49 (around 1613), he appears to have retired to Stratford, where he died three years later. Few records of Shakespeare's private life survive; this has stimulated considerable speculation about such matters as his physical appearance, his sexuality, his religious beliefs, and whether the works attributed to him were written by others. Such theories are often criticised for failing to adequately note that few records survive of most commoners of the period.\n\nShakespeare produced most of his known works between 1589 and 1613. His early plays were primarily comedies and histories and are regarded as some of the best work produced in these genres. Until about 1608, he wrote mainly tragedies, among them Hamlet, Othello, King Lear, and Macbeth, all considered to be among the finest works in the English language. In the last phase of his life, he wrote tragicomedies (also known as romances) and collaborated with other playwrights.\n\nMany of Shakespeare's plays were published in editions of varying quality and accuracy in his lifetime. However, in 1623, two fellow actors and friends of Shakespeare's, John Heminges and Henry Condell, published a more definitive text known as the First Folio, a posthumous collected edition of Shakespeare's dramatic works that included all but two of his plays. The volume was prefaced with a poem by Ben Jonson, in which Jonson presciently hails Shakespeare in a now-famous quote as \"not of an age, but for all time\".\n\nThroughout the 20th and 21st centuries, Shakespeare's works have been continually adapted and rediscovered by new movements in scholarship and performance. His plays remain popular and are studied, performed, and reinterpreted through various cultural and political contexts around the world.", "roomName": "Matrix HQ", "roomAlias": "#matrix:matrix.org", + "spaceName": "Runner's world", "roomTopic": "Welcome to Matrix HQ! Here is the rest of the room topic, with a https://www.example.org url and a phone number: 0102030405 which should not be clickable." }, { @@ -14,6 +15,7 @@ "message": "Hello!", "roomName": "Room name very loooooooong with some details", "roomAlias": "#matrix:matrix.org", + "spaceName": "Matrix Org", "roomTopic": "Room topic very loooooooong with some details" }, { @@ -22,6 +24,7 @@ "message": "How are you?", "roomName": "Room name very loooooooong with some details", "roomAlias": "#matrix:matrix.org", + "spaceName": "Rennes", "roomTopic": "Room topic very loooooooong with some details" }, { @@ -30,6 +33,7 @@ "message": "Great weather today!", "roomName": "Room name very loooooooong with some details", "roomAlias": "#matrix:matrix.org", + "spaceName": "Est London", "roomTopic": "Room topic very loooooooong with some details" }, { @@ -38,6 +42,7 @@ "message": "Let's do a picnic", "roomName": "Room name very loooooooong with some details", "roomAlias": "#matrix:matrix.org", + "spaceName": "Element HQ", "roomTopic": "Room topic very loooooooong with some details" }, { @@ -46,6 +51,7 @@ "message": "Yes, great idea", "roomName": "Room name very loooooooong with some details", "roomAlias": "#matrix:matrix.org", + "spaceName": "My Company", "roomTopic": "Room topic very loooooooong with some details" } ] diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index accf3d89ad..efa1ec7ae6 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -275,6 +275,7 @@ + val powerLevelsHelper = PowerLevelsHelper(powerLevelContent) val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId) + val canAddChild = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) views.invitePeople.isVisible = canInvite + views.addRooms.isVisible = canAddChild }.disposeOnDestroyView() views.invitePeople.views.bottomSheetActionClickableZone.debouncedClicks { @@ -112,6 +116,10 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment( + // Important it must match the PageList builder notify Looper + modelBuildingHandler = createUIHandler(), + + itemDiffCallback = object : DiffUtil.ItemCallback() { + + override fun areItemsTheSame(oldItem: RoomSummary, newItem: RoomSummary): Boolean { + return oldItem.roomId == newItem.roomId + } + + override fun areContentsTheSame(oldItem: RoomSummary, newItem: RoomSummary): Boolean { + // for this use case we can test less things + return oldItem.displayName == newItem.displayName + && oldItem.avatarUrl == newItem.avatarUrl + } + } +) { + + interface Listener { + fun onItemSelected(roomSummary: RoomSummary) + } + + var listener: Listener? = null + var ignoreRooms: List? = null + + var selectedItems: Map = emptyMap() + set(value) { + field = value + // mmm annoying but can't just force for a given model + requestForcedModelBuild() + } + + override fun addModels(models: List>) { + if (ignoreRooms == null) { + super.addModels(models) + } else { + super.addModels( + models.filter { + it !is RoomSelectionItem || !ignoreRooms!!.contains(it.matrixItem.id) + } + ) + } + } + + override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> { + if (item == null) return RoomSelectionPlaceHolderItem_().apply { id(currentPosition) } + return RoomSelectionItem_().apply { + id(item.roomId) + matrixItem(item.toMatrixItem()) + avatarRenderer(this@AddRoomListController.avatarRenderer) + space(item.roomType == RoomType.SPACE) + selected(selectedItems[item.roomId] ?: false) + itemClickListener(DebouncedClickListener({ + listener?.onItemSelected(item) + })) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionItem.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionItem.kt new file mode 100644 index 0000000000..02c5f38226 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionItem.kt @@ -0,0 +1,67 @@ +/* + * 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.manage + +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.core.content.ContextCompat +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.features.home.AvatarRenderer +import org.matrix.android.sdk.api.util.MatrixItem + +@EpoxyModelClass(layout = R.layout.item_room_to_add_in_space) +abstract class RoomSelectionItem : VectorEpoxyModel() { + + @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer + @EpoxyAttribute lateinit var matrixItem: MatrixItem + @EpoxyAttribute var space: Boolean = false + @EpoxyAttribute var selected: Boolean = false + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: View.OnClickListener? = null + + override fun bind(holder: Holder) { + super.bind(holder) + if (space) { + avatarRenderer.renderSpace(matrixItem, holder.avatarImageView) + } else { + avatarRenderer.render(matrixItem, holder.avatarImageView) + } + holder.titleText.text = matrixItem.getBestName() + + if (selected) { + holder.checkboxImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_checkbox_on)) + holder.checkboxImage.contentDescription = holder.view.context.getString(R.string.a11y_checked) + } else { + holder.checkboxImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_checkbox_off)) + holder.checkboxImage.contentDescription = holder.view.context.getString(R.string.a11y_unchecked) + } + + holder.view.setOnClickListener { + itemClickListener?.onClick(it) + } + } + + class Holder : VectorEpoxyHolder() { + val avatarImageView by bind(R.id.itemAddRoomRoomAvatar) + val titleText by bind(R.id.itemAddRoomRoomNameText) + val checkboxImage by bind(R.id.itemAddRoomRoomCheckBox) + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionPlaceHolderItem.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionPlaceHolderItem.kt new file mode 100644 index 0000000000..7c116811b4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionPlaceHolderItem.kt @@ -0,0 +1,27 @@ +/* + * 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.manage + +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel + +@EpoxyModelClass(layout = R.layout.item_room_to_add_in_space_placeholder) +abstract class RoomSelectionPlaceHolderItem : VectorEpoxyModel() { + class Holder : VectorEpoxyHolder() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomActions.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomActions.kt new file mode 100644 index 0000000000..502d63cf89 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomActions.kt @@ -0,0 +1,27 @@ +/* + * 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.manage + +import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.model.RoomSummary + +sealed class SpaceAddRoomActions : VectorViewModelAction { + data class UpdateFilter(val filter: String) : SpaceAddRoomActions() + data class ToggleSelection(val roomSummary: RoomSummary) : SpaceAddRoomActions() + object Save : SpaceAddRoomActions() +// object HandleBack : SpaceAddRoomActions() +} 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 new file mode 100644 index 0000000000..ee14aec120 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt @@ -0,0 +1,171 @@ +/* + * 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.manage + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.activityViewModel +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +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 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 viewModelFactory: SpaceAddRoomsViewModel.Factory +) : VectorBaseFragment(), + OnBackPressed, AddRoomListController.Listener, SpaceAddRoomsViewModel.Factory { + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = + FragmentSpaceAddRoomsBinding.inflate(layoutInflater, container, false) + + private val viewModel by fragmentViewModel(SpaceAddRoomsViewModel::class) + + private val sharedViewModel: SpaceManageSharedViewModel by activityViewModel() + + override fun create(initialState: SpaceAddRoomsState): SpaceAddRoomsViewModel = + viewModelFactory.create(initialState) + + override fun getMenuRes(): Int = R.menu.menu_space_add_room + + private var saveNeeded = false + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + vectorBaseActivity.setSupportActionBar(views.addRoomToSpaceToolbar) + + vectorBaseActivity.supportActionBar?.let { + it.setDisplayShowHomeEnabled(true) + it.setDisplayHomeAsUpEnabled(true) + } + +// sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) + setupRecyclerView() + + views.publicRoomsFilter.queryTextChanges() + .debounce(100, TimeUnit.MILLISECONDS) + .subscribeBy { + viewModel.handle(SpaceAddRoomActions.UpdateFilter(it.toString())) + } + .disposeOnDestroyView() + + viewModel.selectionListLiveData.observe(viewLifecycleOwner) { + epoxyController.selectedItems = it + saveNeeded = it.values.any { it } + invalidateOptionsMenu() + } + + viewModel.selectSubscribe(this, SpaceAddRoomsState::spaceName) { + views.appBarSpaceInfo.text = it + }.disposeOnDestroyView() + + viewModel.selectSubscribe(this, SpaceAddRoomsState::ignoreRooms) { + epoxyController.ignoreRooms = it + }.disposeOnDestroyView() + + viewModel.selectSubscribe(this, SpaceAddRoomsState::isSaving) { + if (it is Loading) { + sharedViewModel.handle(SpaceManagedSharedAction.ShowLoading) + } else { + sharedViewModel.handle(SpaceManagedSharedAction.HideLoading) + } + }.disposeOnDestroyView() + +// views.createNewRoom.debouncedClicks { +// sharedActionViewModel.post(RoomDirectorySharedAction.CreateRoom) +// } + + viewModel.observeViewEvents { + when (it) { + SpaceAddRoomsViewEvents.WarnUnsavedChanged -> { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.dialog_title_warning) + .setMessage(R.string.warning_unsaved_change) + .setPositiveButton(R.string.warning_unsaved_change_discard) { _, _ -> + sharedViewModel.handle(SpaceManagedSharedAction.HandleBack) + } + .setNegativeButton(R.string.cancel, null) + .show() + } + SpaceAddRoomsViewEvents.SaveFailed -> { + invalidateOptionsMenu() + } + SpaceAddRoomsViewEvents.SavedDone -> { + sharedViewModel.handle(SpaceManagedSharedAction.HandleBack) + } + } + } + } + + override fun invalidate() = withState(viewModel) { + super.invalidate() + } + + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + menu.findItem(R.id.spaceAddRoomSaveItem).isVisible = saveNeeded + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == R.id.spaceAddRoomSaveItem) { + viewModel.handle(SpaceAddRoomActions.Save) + return true + } + return super.onOptionsItemSelected(item) + } + + override fun onDestroyView() { + views.roomList.cleanup() + epoxyController.listener = null + super.onDestroyView() + } + + private fun setupRecyclerView() { + views.roomList.configureWith(epoxyController, showDivider = true) + epoxyController.listener = this + viewModel.updatableLivePageResult.livePagedList.observe(viewLifecycleOwner) { + epoxyController.submitList(it) + } + } + + override fun onBackPressed(toolbarButton: Boolean): Boolean { + if (viewModel.canGoBack()) { + sharedViewModel.handle(SpaceManagedSharedAction.HandleBack) + } + return true + } + + override fun onItemSelected(roomSummary: RoomSummary) { + viewModel.handle(SpaceAddRoomActions.ToggleSelection(roomSummary)) + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt new file mode 100644 index 0000000000..6ce3468fe1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt @@ -0,0 +1,35 @@ +/* + * 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.manage + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized + +data class SpaceAddRoomsState( + // The current filter + val spaceId: String = "", + val currentFilter: String = "", + val spaceName: String = "", + val ignoreRooms: List = emptyList(), + val isSaving: Async> = Uninitialized +// val selectionList: Map = emptyMap() +) : MvRxState { + constructor(args: SpaceManageArgs) : this( + spaceId = args.spaceId + ) +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewEvents.kt new file mode 100644 index 0000000000..f0bc0cecc2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewEvents.kt @@ -0,0 +1,25 @@ +/* + * 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.manage + +import im.vector.app.core.platform.VectorViewEvents + +sealed class SpaceAddRoomsViewEvents : VectorViewEvents { + object WarnUnsavedChanged : SpaceAddRoomsViewEvents() + object SavedDone : SpaceAddRoomsViewEvents() + object SaveFailed : SpaceAddRoomsViewEvents() +} 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 new file mode 100644 index 0000000000..e90fc3754c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt @@ -0,0 +1,194 @@ +/* + * 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.manage + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import androidx.paging.PagedList +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.Fail +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.core.platform.VectorViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.query.ActiveSpaceFilter +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.query.RoomCategoryFilter +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.roomSummaryQueryParams + +class AddRoomError(val errorList: Map) : Throwable() + +class SpaceAddRoomsViewModel @AssistedInject constructor( + @Assisted val initialState: SpaceAddRoomsState, + private val session: Session +) : VectorViewModel(initialState) { + + val updatableLivePageResult: UpdatableLivePageResult by lazy { + session.getFilteredPagedRoomSummariesLive( + roomSummaryQueryParams { + this.memberships = listOf(Membership.JOIN) + this.excludeType = null + this.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + 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 + ) + } + + private val selectionList = mutableMapOf() + val selectionListLiveData = MutableLiveData>() + + init { + val spaceSummary = session.getRoomSummary(initialState.spaceId) + setState { + copy( + spaceName = spaceSummary?.displayName ?: "", + ignoreRooms = (spaceSummary?.flattenParentIds ?: emptyList()) + listOf(initialState.spaceId) + ) + } + } + + @AssistedFactory + interface Factory { + fun create(initialState: SpaceAddRoomsState): SpaceAddRoomsViewModel + } + + companion object : MvRxViewModelFactory { + override fun create(viewModelContext: ViewModelContext, state: SpaceAddRoomsState): SpaceAddRoomsViewModel? { + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") + } + } + + fun canGoBack(): Boolean { + val needToSave = selectionList.values.any { it } + if (needToSave) { + _viewEvents.post(SpaceAddRoomsViewEvents.WarnUnsavedChanged) + } + return !needToSave + } + + override fun handle(action: SpaceAddRoomActions) { + when (action) { + is SpaceAddRoomActions.UpdateFilter -> { + updatableLivePageResult.updateQuery { + it.copy( + displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE) + ) + } + setState { + copy( + currentFilter = action.filter + ) + } + } + is SpaceAddRoomActions.ToggleSelection -> { + selectionList[action.roomSummary.roomId] = (selectionList[action.roomSummary.roomId] ?: false).not() + selectionListLiveData.postValue(selectionList.toMap()) + } + SpaceAddRoomActions.Save -> { + doAddSelectedRooms() + } + } + } + + private fun doAddSelectedRooms() { + val childrenToAdd = selectionList.filter { it.value }.keys + if (childrenToAdd.isEmpty()) return // should not happen + + setState { + copy( + isSaving = Loading() + ) + } + viewModelScope.launch { + val errors = mutableMapOf() + val completed = mutableListOf() + childrenToAdd.forEach { roomId -> + try { + session.spaceService().getSpace(initialState.spaceId)!!.addChildren( + roomId = roomId, + viaServers = listOf(session.sessionParams.homeServerHost ?: ""), + order = null + ) + completed.add(roomId) + } catch (failure: Throwable) { + errors[roomId] = failure + } + } + if (errors.isEmpty()) { + // success + withContext(Dispatchers.Main) { + setState { + copy( + isSaving = Success(childrenToAdd.toList()) + ) + } + completed.forEach { + selectionList.remove(it) + } + _viewEvents.post(SpaceAddRoomsViewEvents.SavedDone) + } + } else if (errors.size < childrenToAdd.size) { + // partial success + withContext(Dispatchers.Main) { + setState { + copy( + isSaving = Success(completed) + ) + } + completed.forEach { + selectionList.remove(it) + } + _viewEvents.post(SpaceAddRoomsViewEvents.SavedDone) + } + } else { + // error + withContext(Dispatchers.Main) { + setState { + copy( + isSaving = Fail(AddRoomError(errors)) + ) + } + _viewEvents.post(SpaceAddRoomsViewEvents.SaveFailed) + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt new file mode 100644 index 0000000000..25c659e66f --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt @@ -0,0 +1,94 @@ +/* + * 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.manage + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.os.Parcelable +import androidx.core.view.isVisible +import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.viewModel +import im.vector.app.R +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.commitTransaction +import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.databinding.ActivitySimpleBinding +import kotlinx.parcelize.Parcelize +import javax.inject.Inject + +@Parcelize +data class SpaceManageArgs( + val spaceId: String +) : Parcelable + +class SpaceManageActivity : VectorBaseActivity(), SpaceManageSharedViewModel.Factory { + + @Inject lateinit var sharedViewModelFactory: SpaceManageSharedViewModel.Factory + + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) + } + + override fun getBinding(): ActivitySimpleBinding = ActivitySimpleBinding.inflate(layoutInflater) + + override fun getTitleRes(): Int = R.string.space_add_existing_rooms + + val sharedViewModel: SpaceManageSharedViewModel by viewModel() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + 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, + SpaceAddRoomFragment::class.java, + Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) }, + simpleName + ) + } + } + } + + sharedViewModel.observeViewEvents { + when (it) { + SpaceManagedSharedViewEvents.Finish -> { + finish() + } + SpaceManagedSharedViewEvents.HideLoading -> { + views.simpleActivityWaitingView.isVisible = false + } + SpaceManagedSharedViewEvents.ShowLoading -> { + views.simpleActivityWaitingView.isVisible = true + } + } + } + } + + companion object { + fun newIntent(context: Context, spaceId: String): Intent { + return Intent(context, SpaceManageActivity::class.java).apply { + putExtra(MvRx.KEY_ARG, SpaceManageArgs(spaceId)) + } + } + } + + override fun create(initialState: SpaceManageViewState) = sharedViewModelFactory.create(initialState) +} 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 new file mode 100644 index 0000000000..1d8612f084 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt @@ -0,0 +1,59 @@ +/* + * 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.manage + +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.platform.VectorViewModel +import org.matrix.android.sdk.api.session.Session + +class SpaceManageSharedViewModel @AssistedInject constructor( + @Assisted initialState: SpaceManageViewState, + private val session: Session +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory { + fun create(initialState: SpaceManageViewState): SpaceManageSharedViewModel + } + + companion object : MvRxViewModelFactory { + override fun create(viewModelContext: ViewModelContext, state: SpaceManageViewState): SpaceManageSharedViewModel? { + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") + } + } + + override fun handle(action: SpaceManagedSharedAction) { + when (action) { + SpaceManagedSharedAction.HandleBack -> { + // for now finish + _viewEvents.post(SpaceManagedSharedViewEvents.Finish) + } + SpaceManagedSharedAction.HideLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.HideLoading) + SpaceManagedSharedAction.ShowLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.ShowLoading) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt new file mode 100644 index 0000000000..18c289f3b3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt @@ -0,0 +1,27 @@ +/* + * 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.manage + +import com.airbnb.mvrx.MvRxState + +data class SpaceManageViewState( + val spaceId: String = "" +) : MvRxState { + constructor(args: SpaceManageArgs) : this( + spaceId = args.spaceId + ) +} 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 new file mode 100644 index 0000000000..f3d7104d71 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt @@ -0,0 +1,25 @@ +/* + * 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.manage + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class SpaceManagedSharedAction : VectorViewModelAction { + object HandleBack : SpaceManagedSharedAction() + object ShowLoading: SpaceManagedSharedAction() + object HideLoading: 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 new file mode 100644 index 0000000000..b1a35c3700 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt @@ -0,0 +1,25 @@ +/* + * 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.manage + +import im.vector.app.core.platform.VectorViewEvents + +sealed class SpaceManagedSharedViewEvents : VectorViewEvents { + object Finish : SpaceManagedSharedViewEvents() + object ShowLoading: SpaceManagedSharedViewEvents() + object HideLoading: SpaceManagedSharedViewEvents() +} diff --git a/vector/src/main/res/drawable/ic_checkbox_off.xml b/vector/src/main/res/drawable/ic_checkbox_off.xml new file mode 100644 index 0000000000..d960c56a44 --- /dev/null +++ b/vector/src/main/res/drawable/ic_checkbox_off.xml @@ -0,0 +1,11 @@ + + + diff --git a/vector/src/main/res/drawable/ic_checkbox_on.xml b/vector/src/main/res/drawable/ic_checkbox_on.xml new file mode 100644 index 0000000000..d152dccc29 --- /dev/null +++ b/vector/src/main/res/drawable/ic_checkbox_on.xml @@ -0,0 +1,18 @@ + + + + diff --git a/vector/src/main/res/layout/bottom_sheet_space_settings.xml b/vector/src/main/res/layout/bottom_sheet_space_settings.xml index 35e631326f..053a326b03 100644 --- a/vector/src/main/res/layout/bottom_sheet_space_settings.xml +++ b/vector/src/main/res/layout/bottom_sheet_space_settings.xml @@ -111,6 +111,18 @@ app:titleTextColor="?attr/riotx_text_primary" tools:actionDescription="" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_room_to_add_in_space.xml b/vector/src/main/res/layout/item_room_to_add_in_space.xml new file mode 100644 index 0000000000..7d1c04468c --- /dev/null +++ b/vector/src/main/res/layout/item_room_to_add_in_space.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_room_to_add_in_space_placeholder.xml b/vector/src/main/res/layout/item_room_to_add_in_space_placeholder.xml new file mode 100644 index 0000000000..9ddce4ba18 --- /dev/null +++ b/vector/src/main/res/layout/item_room_to_add_in_space_placeholder.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/menu/menu_space_add_room.xml b/vector/src/main/res/menu/menu_space_add_room.xml new file mode 100644 index 0000000000..d4016536a6 --- /dev/null +++ b/vector/src/main/res/menu/menu_space_add_room.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 4bc0424c32..60683ba862 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2193,6 +2193,8 @@ Name or ID (#example:matrix.org) Search by name or ID + Search Name + Enable swipe to reply in timeline Add a dedicated tab for unread notifications on main screen. @@ -3329,6 +3331,10 @@ Explore rooms + Add rooms Leave Space Are you sure you want to leave the space? + + + Add existing rooms and space