diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index 82f5d2c6f1..57b76bcfcc 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -75,6 +75,7 @@ import im.vector.riotx.features.settings.crosssigning.CrossSigningSettingsFragme import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment import im.vector.riotx.features.settings.push.PushGatewaysFragment +import im.vector.riotx.features.share.IncomingShareFragment import im.vector.riotx.features.signout.soft.SoftLogoutFragment @Module @@ -355,4 +356,9 @@ interface FragmentModule { @FragmentKey(AttachmentsPreviewFragment::class) fun bindAttachmentsPreviewFragment(fragment: AttachmentsPreviewFragment): Fragment + @Binds + @IntoMap + @FragmentKey(IncomingShareFragment::class) + fun bindIncomingShareFragment(fragment: IncomingShareFragment): Fragment + } diff --git a/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt index f553513bfa..24b6bf790d 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt @@ -47,7 +47,6 @@ import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler import im.vector.riotx.features.reactions.data.EmojiDataSource import im.vector.riotx.features.session.SessionListener import im.vector.riotx.features.settings.VectorPreferences -import im.vector.riotx.features.share.ShareRoomListDataSource import im.vector.riotx.features.ui.UiStateRepository import javax.inject.Singleton @@ -97,8 +96,6 @@ interface VectorComponent { fun homeRoomListObservableStore(): HomeRoomListDataSource - fun shareRoomListObservableStore(): ShareRoomListDataSource - fun selectedGroupStore(): SelectedGroupDataSource fun activeSessionObservableStore(): ActiveSessionDataSource diff --git a/vector/src/main/java/im/vector/riotx/features/home/RoomListDisplayMode.kt b/vector/src/main/java/im/vector/riotx/features/home/RoomListDisplayMode.kt index 18b901a967..6d7f49750d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/RoomListDisplayMode.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/RoomListDisplayMode.kt @@ -23,6 +23,5 @@ enum class RoomListDisplayMode(@StringRes val titleRes: Int) { HOME(R.string.bottom_action_home), PEOPLE(R.string.bottom_action_people_x), ROOMS(R.string.bottom_action_rooms), - FILTERED(/* Not used */ 0), - SHARE(/* Not used */ 0) + FILTERED(/* Not used */ 0) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListAction.kt index 801bb48aed..9634bf6a25 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListAction.kt @@ -16,20 +16,18 @@ package im.vector.riotx.features.home.room.list -import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.riotx.core.platform.VectorViewModelAction import im.vector.riotx.features.share.SharedData sealed class RoomListAction : VectorViewModelAction { - data class SelectRoom(val roomSummary: RoomSummary, val enableMultiSelect: Boolean) : RoomListAction() + data class SelectRoom(val roomSummary: RoomSummary) : RoomListAction() data class ToggleCategory(val category: RoomCategory) : RoomListAction() data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListAction() data class RejectInvitation(val roomSummary: RoomSummary) : RoomListAction() data class FilterWith(val filter: String) : RoomListAction() data class ChangeRoomNotificationState(val roomId: String, val notificationState: RoomNotificationState) : RoomListAction() data class LeaveRoom(val roomId: String) : RoomListAction() - data class ShareToSelectedRooms(val sharedData: SharedData, val optionalMessage: String? = null): RoomListAction() object MarkAllRoomsRead : RoomListAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListDisplayModeFilter.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListDisplayModeFilter.kt index 8cae1fd4e8..9b5f74c9e6 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListDisplayModeFilter.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListDisplayModeFilter.kt @@ -33,7 +33,6 @@ class RoomListDisplayModeFilter(private val displayMode: RoomListDisplayMode) : RoomListDisplayMode.PEOPLE -> roomSummary.isDirect && roomSummary.membership == Membership.JOIN RoomListDisplayMode.ROOMS -> !roomSummary.isDirect && roomSummary.membership == Membership.JOIN RoomListDisplayMode.FILTERED -> roomSummary.membership == Membership.JOIN - RoomListDisplayMode.SHARE -> roomSummary.membership == Membership.JOIN } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index 8bb8eeb75c..96af6e5908 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -50,15 +50,13 @@ import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsShare import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import im.vector.riotx.features.home.room.list.widget.FabMenuView import im.vector.riotx.features.notifications.NotificationDrawerManager -import im.vector.riotx.features.share.SharedData import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_list.* import javax.inject.Inject @Parcelize data class RoomListParams( - val displayMode: RoomListDisplayMode, - val sharedData: SharedData? = null + val displayMode: RoomListDisplayMode ) : Parcelable class RoomListFragment @Inject constructor( @@ -110,11 +108,6 @@ class RoomListFragment @Inject constructor( }.exhaustive } - sendShareButton.setOnClickListener { _ -> - roomListViewModel.handle(RoomListAction.ShareToSelectedRooms(roomListParams.sharedData!!)) - requireActivity().finish() - } - createChatFabMenu.listener = this sharedActionViewModel @@ -137,19 +130,7 @@ class RoomListFragment @Inject constructor( } private fun handleSelectRoom(event: RoomListViewEvents.SelectRoom) { - if (roomListParams.displayMode == RoomListDisplayMode.SHARE) { - val sharedData = roomListParams.sharedData ?: return - AlertDialog.Builder(requireActivity()) - .setTitle(R.string.send_attachment) - .setMessage(getString(R.string.share_confirm_room, event.roomSummary.displayName)) - .setPositiveButton(R.string.send) { _, _ -> - navigator.openRoomForSharing(requireActivity(), event.roomSummary.roomId, sharedData) - } - .setNegativeButton(R.string.cancel, null) - .show() - } else { - navigator.openRoom(requireActivity(), event.roomSummary.roomId) - } + navigator.openRoom(requireActivity(), event.roomSummary.roomId) } private fun setupCreateRoomButton() { @@ -268,7 +249,6 @@ class RoomListFragment @Inject constructor( is Fail -> renderFailure(state.asyncFilteredRooms.error) } roomController.update(state) - sendShareButton.isVisible = state.multiSelectionEnabled // Mark all as read menu when (roomListParams.displayMode) { RoomListDisplayMode.HOME, @@ -356,18 +336,14 @@ class RoomListFragment @Inject constructor( // RoomSummaryController.Callback ************************************************************** override fun onRoomClicked(room: RoomSummary) { - roomListViewModel.handle(RoomListAction.SelectRoom(room, enableMultiSelect = false)) + roomListViewModel.handle(RoomListAction.SelectRoom(room)) } override fun onRoomLongClicked(room: RoomSummary): Boolean { - if (roomListParams.displayMode == RoomListDisplayMode.SHARE) { - roomListViewModel.handle(RoomListAction.SelectRoom(room, enableMultiSelect = true)) - } else { - roomController.onRoomLongClicked() - RoomListQuickActionsBottomSheet - .newInstance(room.roomId, RoomListActionsArgs.Mode.FULL) - .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") - } + roomController.onRoomLongClicked() + RoomListQuickActionsBottomSheet + .newInstance(room.roomId, RoomListActionsArgs.Mode.FULL) + .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") return true } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index 7ee641236c..2bfbc6aa8e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -28,7 +28,6 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTag import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.utils.DataSource -import im.vector.riotx.features.home.RoomListDisplayMode import im.vector.riotx.features.share.SharedData import io.reactivex.schedulers.Schedulers import timber.log.Timber @@ -69,38 +68,13 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, is RoomListAction.MarkAllRoomsRead -> handleMarkAllRoomsRead() is RoomListAction.LeaveRoom -> handleLeaveRoom(action) is RoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) - is RoomListAction.ShareToSelectedRooms -> handleShareToSelectedRooms(action) }.exhaustive } - private fun handleShareToSelectedRooms(action: RoomListAction.ShareToSelectedRooms) = withState { - val sharedData = action.sharedData - it.selectedRoomIds.forEach { roomId -> - val room = session.getRoom(roomId) - if (sharedData is SharedData.Text) { - room?.sendTextMessage(sharedData.text) - } else if (sharedData is SharedData.Attachments) { - room?.sendMedias(sharedData.attachmentData) - } - } - } - // PRIVATE METHODS ***************************************************************************** private fun handleSelectRoom(action: RoomListAction.SelectRoom) = withState { - if (it.multiSelectionEnabled) { - val selectedRooms = it.selectedRoomIds - val newSelectedRooms = if (selectedRooms.contains(action.roomSummary.roomId)) { - selectedRooms.minus(action.roomSummary.roomId) - } else { - selectedRooms.plus(action.roomSummary.roomId) - } - setState { copy(multiSelectionEnabled = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) } - } else if (action.enableMultiSelect) { - setState { copy(multiSelectionEnabled = true, selectedRoomIds = setOf(action.roomSummary.roomId)) } - } else { - _viewEvents.post(RoomListViewEvents.SelectRoom(action.roomSummary)) - } + _viewEvents.post(RoomListViewEvents.SelectRoom(action.roomSummary)) } private fun handleToggleCategory(action: RoomListAction.ToggleCategory) = setState { @@ -231,54 +205,35 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, } private fun buildRoomSummaries(rooms: List): RoomSummaries { - if (displayMode == RoomListDisplayMode.SHARE) { - val recentRooms = ArrayList(20) - val otherRooms = ArrayList(rooms.size) + // Set up init size on directChats and groupRooms as they are the biggest ones + val invites = ArrayList() + val favourites = ArrayList() + val directChats = ArrayList(rooms.size) + val groupRooms = ArrayList(rooms.size) + val lowPriorities = ArrayList() + val serverNotices = ArrayList() - rooms - .filter { roomListDisplayModeFilter.test(it) } - .forEach { room -> - when (room.breadcrumbsIndex) { - RoomSummary.NOT_IN_BREADCRUMBS -> otherRooms.add(room) - else -> recentRooms.add(room) - } + rooms + .filter { roomListDisplayModeFilter.test(it) } + .forEach { room -> + val tags = room.tags.map { it.name } + when { + room.membership == Membership.INVITE -> invites.add(room) + tags.contains(RoomTag.ROOM_TAG_SERVER_NOTICE) -> serverNotices.add(room) + tags.contains(RoomTag.ROOM_TAG_FAVOURITE) -> favourites.add(room) + tags.contains(RoomTag.ROOM_TAG_LOW_PRIORITY) -> lowPriorities.add(room) + room.isDirect -> directChats.add(room) + else -> groupRooms.add(room) } + } - return RoomSummaries().apply { - put(RoomCategory.RECENT_ROOMS, recentRooms) - put(RoomCategory.OTHER_ROOMS, otherRooms) - } - } else { - // Set up init size on directChats and groupRooms as they are the biggest ones - val invites = ArrayList() - val favourites = ArrayList() - val directChats = ArrayList(rooms.size) - val groupRooms = ArrayList(rooms.size) - val lowPriorities = ArrayList() - val serverNotices = ArrayList() - - rooms - .filter { roomListDisplayModeFilter.test(it) } - .forEach { room -> - val tags = room.tags.map { it.name } - when { - room.membership == Membership.INVITE -> invites.add(room) - tags.contains(RoomTag.ROOM_TAG_SERVER_NOTICE) -> serverNotices.add(room) - tags.contains(RoomTag.ROOM_TAG_FAVOURITE) -> favourites.add(room) - tags.contains(RoomTag.ROOM_TAG_LOW_PRIORITY) -> lowPriorities.add(room) - room.isDirect -> directChats.add(room) - else -> groupRooms.add(room) - } - } - - return RoomSummaries().apply { - put(RoomCategory.INVITE, invites) - put(RoomCategory.FAVOURITE, favourites) - put(RoomCategory.DIRECT, directChats) - put(RoomCategory.GROUP, groupRooms) - put(RoomCategory.LOW_PRIORITY, lowPriorities) - put(RoomCategory.SERVER_NOTICE, serverNotices) - } + return RoomSummaries().apply { + put(RoomCategory.INVITE, invites) + put(RoomCategory.FAVOURITE, favourites) + put(RoomCategory.DIRECT, directChats) + put(RoomCategory.GROUP, groupRooms) + put(RoomCategory.LOW_PRIORITY, lowPriorities) + put(RoomCategory.SERVER_NOTICE, serverNotices) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModelFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModelFactory.kt index 60ec92d8cf..5b823d6444 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModelFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModelFactory.kt @@ -18,21 +18,18 @@ package im.vector.riotx.features.home.room.list import im.vector.matrix.android.api.session.Session import im.vector.riotx.features.home.HomeRoomListDataSource -import im.vector.riotx.features.home.RoomListDisplayMode -import im.vector.riotx.features.share.ShareRoomListDataSource import javax.inject.Inject import javax.inject.Provider class RoomListViewModelFactory @Inject constructor(private val session: Provider, - private val homeRoomListDataSource: Provider, - private val shareRoomListDataSource: Provider) + private val homeRoomListDataSource: Provider) : RoomListViewModel.Factory { override fun create(initialState: RoomListViewState): RoomListViewModel { return RoomListViewModel( initialState, session.get(), - if (initialState.displayMode == RoomListDisplayMode.SHARE) shareRoomListDataSource.get() else homeRoomListDataSource.get() + homeRoomListDataSource.get() ) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt index e660cc47c0..b41b4b9eeb 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt @@ -43,12 +43,7 @@ data class RoomListViewState( val isDirectRoomsExpanded: Boolean = true, val isGroupRoomsExpanded: Boolean = true, val isLowPriorityRoomsExpanded: Boolean = true, - val isServerNoticeRoomsExpanded: Boolean = true, - // For sharing - val isRecentExpanded: Boolean = true, - val isOtherExpanded: Boolean = true, - val selectedRoomIds: Set = emptySet(), - val multiSelectionEnabled: Boolean = false + val isServerNoticeRoomsExpanded: Boolean = true ) : MvRxState { constructor(args: RoomListParams) : this(displayMode = args.displayMode) @@ -61,8 +56,6 @@ data class RoomListViewState( RoomCategory.GROUP -> isGroupRoomsExpanded RoomCategory.LOW_PRIORITY -> isLowPriorityRoomsExpanded RoomCategory.SERVER_NOTICE -> isServerNoticeRoomsExpanded - RoomCategory.RECENT_ROOMS -> isRecentExpanded - RoomCategory.OTHER_ROOMS -> isOtherExpanded } } @@ -74,8 +67,6 @@ data class RoomListViewState( RoomCategory.GROUP -> copy(isGroupRoomsExpanded = !isGroupRoomsExpanded) RoomCategory.LOW_PRIORITY -> copy(isLowPriorityRoomsExpanded = !isLowPriorityRoomsExpanded) RoomCategory.SERVER_NOTICE -> copy(isServerNoticeRoomsExpanded = !isServerNoticeRoomsExpanded) - RoomCategory.RECENT_ROOMS -> copy(isRecentExpanded = !isRecentExpanded) - RoomCategory.OTHER_ROOMS -> copy(isOtherExpanded = !isOtherExpanded) } } @@ -95,11 +86,7 @@ enum class RoomCategory(@StringRes val titleRes: Int) { DIRECT(R.string.bottom_action_people_x), GROUP(R.string.bottom_action_rooms), LOW_PRIORITY(R.string.low_priority_header), - SERVER_NOTICE(R.string.system_alerts_header), - - // For Sharing - RECENT_ROOMS(R.string.room_list_sharing_header_recent_rooms), - OTHER_ROOMS(R.string.room_list_sharing_header_other_rooms) + SERVER_NOTICE(R.string.system_alerts_header) } fun RoomSummaries?.isNullOrEmpty(): Boolean { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt index eb05802d6e..da64c87fb7 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt @@ -60,7 +60,6 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri val nonNullViewState = viewState ?: return when (nonNullViewState.displayMode) { RoomListDisplayMode.FILTERED -> buildFilteredRooms(nonNullViewState) - RoomListDisplayMode.SHARE -> buildShareRooms(nonNullViewState) else -> buildRooms(nonNullViewState) } } @@ -78,44 +77,11 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri viewState.joiningErrorRoomsIds, viewState.rejectingRoomsIds, viewState.rejectingErrorRoomsIds, - viewState.selectedRoomIds) + emptySet()) addFilterFooter(viewState) } - private fun buildShareRooms(viewState: RoomListViewState) { - var hasResult = false - val roomSummaries = viewState.asyncFilteredRooms() - roomListNameFilter.filter = viewState.roomFilter - - roomSummaries?.forEach { (category, summaries) -> - val filteredSummaries = summaries - .filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) } - - if (filteredSummaries.isEmpty()) { - return@forEach - } else { - hasResult = true - val isExpanded = viewState.isCategoryExpanded(category) - buildRoomCategory(viewState, emptyList(), category.titleRes, viewState.isCategoryExpanded(category)) { - listener?.onToggleRoomCategory(category) - } - if (isExpanded) { - buildRoomModels(filteredSummaries, - emptySet(), - emptySet(), - emptySet(), - emptySet(), - viewState.selectedRoomIds - ) - } - } - } - if (!hasResult) { - addNoResultItem() - } - } - private fun buildRooms(viewState: RoomListViewState) { var showHelp = false val roomSummaries = viewState.asyncFilteredRooms() @@ -133,7 +99,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri viewState.joiningErrorRoomsIds, viewState.rejectingRoomsIds, viewState.rejectingErrorRoomsIds, - viewState.selectedRoomIds) + emptySet()) // Never set showHelp to true for invitation if (category != RoomCategory.INVITE) { showHelp = userPreferencesProvider.shouldShowLongClickOnRoomHelp() @@ -162,13 +128,6 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri } } - private fun addNoResultItem() { - noResultItem { - id("no_result") - text(stringProvider.getString(R.string.no_result_placeholder)) - } - } - private fun buildRoomCategory(viewState: RoomListViewState, summaries: List, @StringRes titleRes: Int, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt index b61f8fcbe5..baa93f96e3 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt @@ -25,7 +25,6 @@ import im.vector.riotx.R import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.extensions.localDateTime -import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.resources.DateProvider import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.DebouncedClickListener @@ -50,16 +49,16 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { return when (roomSummary.membership) { Membership.INVITE -> createInvitationItem(roomSummary, joiningRoomsIds, joiningErrorRoomsIds, rejectingRoomsIds, rejectingErrorRoomsIds, listener) - else -> createRoomItem(roomSummary, selectedRoomIds, listener) + else -> createRoomItem(roomSummary, selectedRoomIds, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked }) } } - private fun createInvitationItem(roomSummary: RoomSummary, - joiningRoomsIds: Set, - joiningErrorRoomsIds: Set, - rejectingRoomsIds: Set, - rejectingErrorRoomsIds: Set, - listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { + fun createInvitationItem(roomSummary: RoomSummary, + joiningRoomsIds: Set, + joiningErrorRoomsIds: Set, + rejectingRoomsIds: Set, + rejectingErrorRoomsIds: Set, + listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { val secondLine = if (roomSummary.isDirect) { roomSummary.latestPreviewableEvent?.root?.senderId } else { @@ -82,7 +81,12 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor .listener { listener?.onRoomClicked(roomSummary) } } - private fun createRoomItem(roomSummary: RoomSummary, selectedRoomIds: Set, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { + fun createRoomItem( + roomSummary: RoomSummary, + selectedRoomIds: Set, + onClick: ((RoomSummary) -> Unit)?, + onLongClick: ((RoomSummary) -> Boolean)? + ): VectorEpoxyModel<*> { val unreadCount = roomSummary.notificationCount val showHighlighted = roomSummary.highlightCount > 0 val showSelected = selectedRoomIds.contains(roomSummary.roomId) @@ -124,11 +128,11 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor .hasUnreadMessage(roomSummary.hasUnreadMessages) .hasDraft(roomSummary.userDrafts.isNotEmpty()) .itemLongClickListener { _ -> - listener?.onRoomLongClicked(roomSummary) ?: false + onLongClick?.invoke(roomSummary) ?: false } .itemClickListener( DebouncedClickListener(View.OnClickListener { _ -> - listener?.onRoomClicked(roomSummary) + onClick?.invoke(roomSummary) }) ) } diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareAction.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareAction.kt new file mode 100644 index 0000000000..af98d3195e --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareAction.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.share + +import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.riotx.core.platform.VectorViewModelAction + +sealed class IncomingShareAction: VectorViewModelAction { + data class SelectRoom(val roomSummary: RoomSummary, val enableMultiSelect: Boolean) : IncomingShareAction() + object ShareToSelectedRooms: IncomingShareAction() + data class FilterWith(val filter: String) : IncomingShareAction() + data class UpdateSharedData(val sharedData: SharedData): IncomingShareAction() +} diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt index b9a9bdc103..61599a61d8 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt @@ -9,127 +9,31 @@ * * 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. + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.V * See the License for the specific language governing permissions and * limitations under the License. */ package im.vector.riotx.features.share -import android.content.ClipDescription -import android.content.Intent -import android.os.Bundle -import android.view.Window -import android.widget.Toast -import androidx.appcompat.widget.SearchView -import com.airbnb.mvrx.viewModel -import com.kbeanie.multipicker.utils.IntentUtils -import im.vector.matrix.android.api.session.content.ContentAttachmentData +import androidx.appcompat.widget.Toolbar import im.vector.riotx.R -import im.vector.riotx.core.di.ActiveSessionHolder -import im.vector.riotx.core.di.ScreenComponent -import im.vector.riotx.core.extensions.replaceFragment +import im.vector.riotx.core.extensions.addFragment +import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.VectorBaseActivity -import im.vector.riotx.features.attachments.AttachmentsHelper -import im.vector.riotx.features.home.LoadingFragment -import im.vector.riotx.features.home.RoomListDisplayMode -import im.vector.riotx.features.home.room.list.RoomListFragment -import im.vector.riotx.features.home.room.list.RoomListParams -import im.vector.riotx.features.login.LoginActivity -import kotlinx.android.synthetic.main.activity_incoming_share.* -import javax.inject.Inject -class IncomingShareActivity : - VectorBaseActivity(), AttachmentsHelper.Callback { +class IncomingShareActivity : VectorBaseActivity(), ToolbarConfigurable { - @Inject lateinit var sessionHolder: ActiveSessionHolder - @Inject lateinit var incomingShareViewModelFactory: IncomingShareViewModel.Factory - private lateinit var attachmentsHelper: AttachmentsHelper - // Do not remove, even if not used, it instantiates the view model - @Suppress("unused") - private val viewModel: IncomingShareViewModel by viewModel() - private val roomListFragment: RoomListFragment? - get() { - return supportFragmentManager.findFragmentById(R.id.shareRoomListFragmentContainer) as? RoomListFragment - } + override fun getLayoutRes() = R.layout.activity_simple - override fun getLayoutRes() = R.layout.activity_incoming_share - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - // If we are not logged in, stop the sharing process and open login screen. - // In the future, we might want to relaunch the sharing process after login. - if (!sessionHolder.hasActiveSession()) { - startLoginActivity() - return - } - configureToolbar(incomingShareToolbar) + override fun initUiAndData() { if (isFirstCreation()) { - replaceFragment(R.id.shareRoomListFragmentContainer, LoadingFragment::class.java) + addFragment(R.id.simpleFragmentContainer, IncomingShareFragment::class.java) } - attachmentsHelper = AttachmentsHelper.create(this, this).register() - if (intent?.action == Intent.ACTION_SEND || intent?.action == Intent.ACTION_SEND_MULTIPLE) { - var isShareManaged = attachmentsHelper.handleShareIntent( - IntentUtils.getPickerIntentForSharing(intent) - ) - if (!isShareManaged) { - isShareManaged = handleTextShare(intent) - } - if (!isShareManaged) { - cannotManageShare() - } - } else { - cannotManageShare() - } - - incomingShareSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String): Boolean { - return true - } - - override fun onQueryTextChange(newText: String): Boolean { - roomListFragment?.filterRoomsWith(newText) - return true - } - }) } - override fun onContentAttachmentsReady(attachments: List) { - val roomListParams = RoomListParams(RoomListDisplayMode.SHARE, sharedData = SharedData.Attachments(attachments)) - replaceFragment(R.id.shareRoomListFragmentContainer, RoomListFragment::class.java, roomListParams) - } - - override fun onAttachmentsProcessFailed() { - cannotManageShare() - } - - private fun cannotManageShare() { - Toast.makeText(this, R.string.error_handling_incoming_share, Toast.LENGTH_LONG).show() - finish() - } - - private fun handleTextShare(intent: Intent): Boolean { - if (intent.type == ClipDescription.MIMETYPE_TEXT_PLAIN) { - val sharedText = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)?.toString() - return if (sharedText.isNullOrEmpty()) { - false - } else { - val roomListParams = RoomListParams(RoomListDisplayMode.SHARE, sharedData = SharedData.Text(sharedText)) - replaceFragment(R.id.shareRoomListFragmentContainer, RoomListFragment::class.java, roomListParams) - true - } - } - return false - } - - private fun startLoginActivity() { - val intent = LoginActivity.newIntent(this, null) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) - startActivity(intent) - finish() + override fun configure(toolbar: Toolbar) { + configureToolbar(toolbar, displayBack = false) } } + diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareController.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareController.kt new file mode 100644 index 0000000000..4e7f585458 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareController.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.share + +import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Incomplete +import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.loadingItem +import im.vector.riotx.core.epoxy.noResultItem +import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.features.home.room.list.RoomSummaryItemFactory +import javax.inject.Inject + +class IncomingShareController @Inject constructor(private val roomSummaryItemFactory: RoomSummaryItemFactory, + private val stringProvider: StringProvider) : TypedEpoxyController() { + + interface Callback { + fun onRoomClicked(roomSummary: RoomSummary) + fun onRoomLongClicked(roomSummary: RoomSummary): Boolean + } + + var callback: Callback? = null + + override fun buildModels(data: IncomingShareViewState) { + if (data.sharedData == null || data.filteredRoomSummaries is Incomplete) { + loadingItem { + id("loading") + } + return + } + val roomSummaries = data.filteredRoomSummaries() + if (roomSummaries.isNullOrEmpty()) { + noResultItem { + id("no_result") + text(stringProvider.getString(R.string.no_result_placeholder)) + } + } else { + roomSummaries.forEach { roomSummary -> + roomSummaryItemFactory + .createRoomItem(roomSummary, data.selectedRoomIds, callback?.let { it::onRoomClicked }, callback?.let { it::onRoomLongClicked }) + .addTo(this) + } + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt new file mode 100644 index 0000000000..a107f3f698 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.share + +import android.content.ClipDescription +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.widget.SearchView +import androidx.core.view.isVisible +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import com.kbeanie.multipicker.utils.IntentUtils +import im.vector.matrix.android.api.session.content.ContentAttachmentData +import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.riotx.R +import im.vector.riotx.core.di.ActiveSessionHolder +import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith +import im.vector.riotx.core.extensions.exhaustive +import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.features.attachments.AttachmentsHelper +import im.vector.riotx.features.login.LoginActivity +import kotlinx.android.synthetic.main.fragment_incoming_share.* +import javax.inject.Inject + +class IncomingShareFragment @Inject constructor( + val incomingShareViewModelFactory: IncomingShareViewModel.Factory, + private val incomingShareController: IncomingShareController, + private val sessionHolder: ActiveSessionHolder +) : VectorBaseFragment(), AttachmentsHelper.Callback, IncomingShareController.Callback { + + private lateinit var attachmentsHelper: AttachmentsHelper + private val incomingShareViewModel: IncomingShareViewModel by fragmentViewModel() + + override fun getLayoutResId() = R.layout.fragment_incoming_share + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + // If we are not logged in, stop the sharing process and open login screen. + // In the future, we might want to relaunch the sharing process after login. + if (!sessionHolder.hasActiveSession()) { + startLoginActivity() + return + } + super.onViewCreated(view, savedInstanceState) + setupRecyclerView() + setupToolbar(incomingShareToolbar) + attachmentsHelper = AttachmentsHelper.create(this, this).register() + + val intent = vectorBaseActivity.intent + if (intent?.action == Intent.ACTION_SEND || intent?.action == Intent.ACTION_SEND_MULTIPLE) { + var isShareManaged = attachmentsHelper.handleShareIntent( + IntentUtils.getPickerIntentForSharing(intent) + ) + if (!isShareManaged) { + isShareManaged = handleTextShare(intent) + } + if (!isShareManaged) { + cannotManageShare() + } + } else { + cannotManageShare() + } + + incomingShareSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + return true + } + + override fun onQueryTextChange(newText: String): Boolean { + incomingShareViewModel.handle(IncomingShareAction.FilterWith(newText)) + return true + } + }) + sendShareButton.setOnClickListener { _ -> + handleSendShare() + } + incomingShareViewModel.observeViewEvents { + when (it) { + is IncomingShareViewEvents.ShareToRoom -> handleShareToRoom(it) + }.exhaustive + } + } + + private fun handleShareToRoom(event: IncomingShareViewEvents.ShareToRoom) { + if (event.showAlert) { + showConfirmationDialog(event.roomSummary, event.sharedData) + } else { + navigator.openRoomForSharing(requireActivity(), event.roomSummary.roomId, event.sharedData) + } + } + + private fun handleSendShare() { + incomingShareViewModel.handle(IncomingShareAction.ShareToSelectedRooms) + } + + override fun onDestroyView() { + incomingShareController.callback = null + incomingShareRoomList.cleanup() + super.onDestroyView() + } + + private fun setupRecyclerView() { + incomingShareRoomList.configureWith(incomingShareController, hasFixedSize = true) + incomingShareController.callback = this + } + + override fun onContentAttachmentsReady(attachments: List) { + val sharedData = SharedData.Attachments(attachments) + incomingShareViewModel.handle(IncomingShareAction.UpdateSharedData(sharedData)) + } + + override fun onAttachmentsProcessFailed() { + cannotManageShare() + } + + private fun cannotManageShare() { + Toast.makeText(requireContext(), R.string.error_handling_incoming_share, Toast.LENGTH_LONG).show() + requireActivity().finish() + } + + private fun handleTextShare(intent: Intent): Boolean { + if (intent.type == ClipDescription.MIMETYPE_TEXT_PLAIN) { + val sharedText = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)?.toString() + return if (sharedText.isNullOrEmpty()) { + false + } else { + val sharedData = SharedData.Text(sharedText) + incomingShareViewModel.handle(IncomingShareAction.UpdateSharedData(sharedData)) + true + } + } + return false + } + + private fun showConfirmationDialog(roomSummary: RoomSummary, sharedData: SharedData) { + AlertDialog.Builder(requireActivity()) + .setTitle(R.string.send_attachment) + .setMessage(getString(R.string.share_confirm_room, roomSummary.displayName)) + .setPositiveButton(R.string.send) { _, _ -> + navigator.openRoomForSharing(requireActivity(), roomSummary.roomId, sharedData) + } + .setNegativeButton(R.string.cancel, null) + .show() + } + + private fun startLoginActivity() { + val intent = LoginActivity.newIntent(requireActivity(), null) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + requireActivity().finish() + } + + override fun invalidate() = withState(incomingShareViewModel) { + sendShareButton.isVisible = it.multiSelectionEnabled + incomingShareController.setData(it) + } + + override fun onRoomClicked(roomSummary: RoomSummary) { + incomingShareViewModel.handle(IncomingShareAction.SelectRoom(roomSummary, false)) + } + + override fun onRoomLongClicked(roomSummary: RoomSummary): Boolean { + incomingShareViewModel.handle(IncomingShareAction.SelectRoom(roomSummary, true)) + return true + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/share/ShareRoomListDataSource.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewEvents.kt similarity index 64% rename from vector/src/main/java/im/vector/riotx/features/share/ShareRoomListDataSource.kt rename to vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewEvents.kt index b1b4d7b46e..ebd6997760 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/ShareRoomListDataSource.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewEvents.kt @@ -1,11 +1,11 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright (c) 2020 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 + * 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, @@ -17,9 +17,8 @@ package im.vector.riotx.features.share import im.vector.matrix.android.api.session.room.model.RoomSummary -import im.vector.riotx.core.utils.BehaviorDataSource -import javax.inject.Inject -import javax.inject.Singleton +import im.vector.riotx.core.platform.VectorViewEvents -@Singleton -class ShareRoomListDataSource @Inject constructor() : BehaviorDataSource>() +sealed class IncomingShareViewEvents : VectorViewEvents { + data class ShareToRoom(val roomSummary: RoomSummary, val sharedData: SharedData, val showAlert: Boolean) : IncomingShareViewEvents() +} diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt index 8b791fbf1b..381756ddaf 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt @@ -16,71 +16,127 @@ package im.vector.riotx.features.share -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext +import com.jakewharton.rxrelay2.BehaviorRelay import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.query.QueryStringValue +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.roomSummaryQueryParams import im.vector.matrix.rx.rx -import im.vector.riotx.ActiveSessionDataSource -import im.vector.riotx.core.platform.EmptyAction -import im.vector.riotx.core.platform.EmptyViewEvents +import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.features.home.room.list.BreadcrumbsRoomComparator -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers +import im.vector.riotx.features.home.room.list.ChronologicalRoomComparator import java.util.concurrent.TimeUnit -data class IncomingShareState(private val dummy: Boolean = false) : MvRxState - -/** - * View model used to observe the room list and post update to the ShareRoomListObservableStore - */ -class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: IncomingShareState, - private val sessionObservableStore: ActiveSessionDataSource, - private val shareRoomListObservableStore: ShareRoomListDataSource, - private val breadcrumbsRoomComparator: BreadcrumbsRoomComparator) - : VectorViewModel(initialState) { +class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: IncomingShareViewState, + private val session: Session, + private val chronologicalRoomComparator: ChronologicalRoomComparator) + : VectorViewModel(initialState) { @AssistedInject.Factory interface Factory { - fun create(initialState: IncomingShareState): IncomingShareViewModel + fun create(initialState: IncomingShareViewState): IncomingShareViewModel } - companion object : MvRxViewModelFactory { + companion object : MvRxViewModelFactory { @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: IncomingShareState): IncomingShareViewModel? { - val activity: IncomingShareActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.incomingShareViewModelFactory.create(state) + override fun create(viewModelContext: ViewModelContext, state: IncomingShareViewState): IncomingShareViewModel? { + val fragment: IncomingShareFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.incomingShareViewModelFactory.create(state) } } + private val filterStream: BehaviorRelay = BehaviorRelay.createDefault("") + init { observeRoomSummaries() } private fun observeRoomSummaries() { - val queryParams = roomSummaryQueryParams() - sessionObservableStore.observe() - .observeOn(AndroidSchedulers.mainThread()) - .switchMap { - it.orNull()?.rx()?.liveRoomSummaries(queryParams) - ?: Observable.just(emptyList()) + val queryParams = roomSummaryQueryParams { + memberships = listOf(Membership.JOIN) + } + session + .rx().liveRoomSummaries(queryParams) + .execute { + copy(roomSummaries = it) + } + + filterStream + .switchMap { filter -> + val displayNameQuery = if (filter.isEmpty()) { + QueryStringValue.NoCondition + } else { + QueryStringValue.Contains(filter, QueryStringValue.Case.INSENSITIVE) + } + val filterQueryParams = roomSummaryQueryParams { + displayName = displayNameQuery + memberships = listOf(Membership.JOIN) + } + session.rx().liveRoomSummaries(filterQueryParams) } .throttleLast(300, TimeUnit.MILLISECONDS) - .map { - it.sortedWith(breadcrumbsRoomComparator) + .map { it.sortedWith(chronologicalRoomComparator) } + .execute { + copy(filteredRoomSummaries = it) } - .subscribe { - shareRoomListObservableStore.post(it) - } - .disposeOnClear() } - override fun handle(action: EmptyAction) { - // No op + override fun handle(action: IncomingShareAction) { + when (action) { + is IncomingShareAction.SelectRoom -> handleSelectRoom(action) + is IncomingShareAction.ShareToSelectedRooms -> handleShareToSelectedRooms() + is IncomingShareAction.FilterWith -> handleFilter(action) + is IncomingShareAction.UpdateSharedData -> handleUpdateSharedData(action) + }.exhaustive + } + + private fun handleUpdateSharedData(action: IncomingShareAction.UpdateSharedData) { + setState { copy(sharedData = action.sharedData) } + } + + private fun handleFilter(action: IncomingShareAction.FilterWith) { + filterStream.accept(action.filter) + } + + private fun handleShareToSelectedRooms() = withState { state -> + val sharedData = state.sharedData ?: return@withState + if (state.selectedRoomIds.size == 1) { + val selectedRoomId = state.selectedRoomIds.first() + val selectedRoom = state.roomSummaries()?.find { it.roomId == selectedRoomId } ?: return@withState + _viewEvents.post(IncomingShareViewEvents.ShareToRoom(selectedRoom, sharedData, showAlert = false)) + } else { + state.selectedRoomIds.forEach { roomId -> + val room = session.getRoom(roomId) + if (sharedData is SharedData.Text) { + room?.sendTextMessage(sharedData.text) + } else if (sharedData is SharedData.Attachments) { + room?.sendMedias(sharedData.attachmentData) + } + } + } + } + + private fun handleSelectRoom(action: IncomingShareAction.SelectRoom) = withState { + if (it.multiSelectionEnabled) { + val selectedRooms = it.selectedRoomIds + val newSelectedRooms = if (selectedRooms.contains(action.roomSummary.roomId)) { + selectedRooms.minus(action.roomSummary.roomId) + } else { + selectedRooms.plus(action.roomSummary.roomId) + } + setState { copy(multiSelectionEnabled = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) } + } else if (action.enableMultiSelect) { + setState { copy(multiSelectionEnabled = true, selectedRoomIds = setOf(action.roomSummary.roomId)) } + } else { + val sharedData = it.sharedData ?: return@withState + _viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, showAlert = true)) + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewState.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewState.kt new file mode 100644 index 0000000000..cad1dd7d9c --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewState.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.share + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.matrix.android.api.session.room.model.RoomSummary + +data class IncomingShareViewState( + val sharedData: SharedData? = null, + val roomSummaries: Async> = Uninitialized, + val filteredRoomSummaries: Async> = Uninitialized, + val selectedRoomIds: Set = emptySet(), + val multiSelectionEnabled: Boolean = false +) : MvRxState + + diff --git a/vector/src/main/res/layout/fragment_incoming_share.xml b/vector/src/main/res/layout/fragment_incoming_share.xml new file mode 100644 index 0000000000..51162835fc --- /dev/null +++ b/vector/src/main/res/layout/fragment_incoming_share.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_room_list.xml b/vector/src/main/res/layout/fragment_room_list.xml index 54e6db72e7..2c828c8397 100644 --- a/vector/src/main/res/layout/fragment_room_list.xml +++ b/vector/src/main/res/layout/fragment_room_list.xml @@ -55,17 +55,4 @@ tools:layout_marginEnd="144dp" tools:visibility="visible" /> - -