diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index ea35bbc087..884433431b 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -158,18 +158,6 @@ class DefaultNavigator @Inject constructor( } } - override fun openNotJoinedRoom(context: Context, roomId: String, eventId: String?, roomSummary: RoomSummary?, buildTask: Boolean) { - val roomPreviewData = RoomPreviewData( - roomId = roomId, - eventId = eventId, - roomAlias = roomSummary?.canonicalAlias, - roomName = roomSummary?.displayName, - avatarUrl = roomSummary?.avatarUrl - ) - val intent = RoomPreviewActivity.newIntent(context, roomPreviewData) - context.startActivity(intent) - } - override fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean) { if (context is VectorBaseActivity) { context.notImplemented("Open group detail") @@ -192,7 +180,12 @@ class DefaultNavigator @Inject constructor( } override fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) { - val intent = RoomPreviewActivity.getIntent(context, publicRoom, roomDirectoryData) + val intent = RoomPreviewActivity.newIntent(context, publicRoom, roomDirectoryData) + context.startActivity(intent) + } + + override fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData) { + val intent = RoomPreviewActivity.newIntent(context, roomPreviewData) context.startActivity(intent) } diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index b26c2711c7..506da5bd3d 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -25,6 +25,7 @@ import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes import im.vector.app.features.media.AttachmentData import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinMode +import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData import im.vector.app.features.terms.ReviewTermsActivity @@ -51,10 +52,10 @@ interface Navigator { fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData) - fun openNotJoinedRoom(context: Context, roomId: String, eventId: String? = null, roomSummary: RoomSummary?, buildTask: Boolean = false) - fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) + fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData) + fun openCreateRoom(context: Context, initialName: String = "") fun openCreateDirectRoom(context: Context) @@ -106,4 +107,5 @@ interface Navigator { view: View, inMemory: List = emptyList(), options: ((MutableList>) -> Unit)?) + } diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index 8ab716d01d..cd26027f94 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -18,9 +18,11 @@ package im.vector.app.features.permalink import android.content.Context import android.net.Uri +import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.toast import im.vector.app.features.navigation.Navigator +import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -61,7 +63,13 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti .map { val roomId = it.getOrNull() if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId) != true) { - openRoom(context, roomId, permalinkData.eventId, buildTask) + openRoom( + context = context, + roomId = roomId, + roomAlias = permalinkData.getRoomAliasOrNull(), + eventId = permalinkData.eventId, + buildTask = buildTask + ) } true } @@ -79,7 +87,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti is PermalinkData.FallbackLink -> { Single.just(false) } - } + }.onErrorReturnItem(false) } private fun PermalinkData.RoomLink.getRoomId(): Single> { @@ -91,13 +99,21 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti } } + private fun PermalinkData.RoomLink.getRoomAliasOrNull(): String? { + return if (isRoomAlias) { + roomIdOrAlias + } else { + null + } + } + /** * Open room either joined, or not */ - private fun openRoom(context: Context, roomId: String?, eventId: String?, buildTask: Boolean) { + private fun openRoom(context: Context, roomId: String?, roomAlias: String?, eventId: String?, buildTask: Boolean) { val session = activeSessionHolder.getSafeActiveSession() ?: return if (roomId == null) { - context.toast("Couldn't get roomId in permalink data.") + context.toast(R.string.room_error_not_found) return } val roomSummary = session.getRoomSummary(roomId) @@ -106,10 +122,18 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti navigator.openRoom(context, roomId, eventId, buildTask) } roomSummary?.membership != Membership.BAN -> { - navigator.openNotJoinedRoom(context, roomId, eventId, roomSummary, buildTask) + val roomPreviewData = RoomPreviewData( + roomId = roomId, + eventId = eventId, + roomAlias = roomAlias ?: roomSummary?.canonicalAlias, + roomName = roomSummary?.displayName, + avatarUrl = roomSummary?.avatarUrl, + buildTask = buildTask + ) + navigator.openRoomPreview(context, roomPreviewData) } else -> { - context.toast("Can't open a room where you are banned from.") + context.toast(R.string.error_opening_banned_room) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt index c60336c1af..18b92abdcf 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt @@ -28,8 +28,10 @@ import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom +import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -42,18 +44,23 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri override fun buildModels(viewState: PublicRoomsViewState) { val publicRooms = viewState.publicRooms - if (publicRooms.isEmpty() - && viewState.asyncPublicRoomsRequest is Success) { + val unknownRoomItem = viewState.buildUnknownRoomIfNeeded() + + val noResult = publicRooms.isEmpty() && viewState.asyncPublicRoomsRequest is Success && unknownRoomItem == null + if (noResult) { // No result noResultItem { id("noResult") text(stringProvider.getString(R.string.no_result_placeholder)) } } else { + publicRooms.forEach { buildPublicRoom(it, viewState) } + unknownRoomItem?.addTo(this) + if ((viewState.hasMore && viewState.asyncPublicRoomsRequest is Success) || viewState.asyncPublicRoomsRequest is Incomplete) { loadingItem { @@ -109,7 +116,29 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri } } + private fun PublicRoomsViewState.buildUnknownRoomIfNeeded(): UnknownRoomItem? { + val roomIdOrAlias = currentFilter.trim() + val isAlias = MatrixPatterns.isRoomAlias(roomIdOrAlias) && !publicRooms.any { it.canonicalAlias == roomIdOrAlias } + val isRoomId = !isAlias && MatrixPatterns.isRoomId(roomIdOrAlias) && !publicRooms.any { it.roomId == roomIdOrAlias } + val roomItem = when { + isAlias -> MatrixItem.RoomAliasItem(roomIdOrAlias, roomIdOrAlias) + isRoomId -> MatrixItem.RoomItem(roomIdOrAlias) + else -> null + } + return roomItem?.let { + UnknownRoomItem_().apply { + id(roomIdOrAlias) + matrixItem(roomItem) + avatarRenderer(this@PublicRoomsController.avatarRenderer) + globalListener { + callback?.onUnknownRoomClicked(roomIdOrAlias) + } + } + } + } + interface Callback { + fun onUnknownRoomClicked(roomIdOrAlias: String) fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) fun onPublicRoomJoin(publicRoom: PublicRoom) fun loadMore() diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt index 57006af6f1..b180018480 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt @@ -29,9 +29,13 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.trackItemsVisibilityChange import im.vector.app.core.platform.VectorBaseFragment -import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom +import im.vector.app.core.utils.toast +import im.vector.app.features.permalink.NavigationInterceptor +import im.vector.app.features.permalink.PermalinkHandler import io.reactivex.rxkotlin.subscribeBy import kotlinx.android.synthetic.main.fragment_public_rooms.* +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -41,7 +45,9 @@ import javax.inject.Inject * - When filtering more (when entering new chars), we could filter on result we already have, during the new server request, to avoid empty screen effect */ class PublicRoomsFragment @Inject constructor( - private val publicRoomsController: PublicRoomsController + private val publicRoomsController: PublicRoomsController, + private val permalinkHandler: PermalinkHandler, + private val session: Session ) : VectorBaseFragment(), PublicRoomsController.Callback { private val viewModel: RoomDirectoryViewModel by activityViewModel() @@ -112,6 +118,23 @@ class PublicRoomsFragment @Inject constructor( publicRoomsController.callback = this } + override fun onUnknownRoomClicked(roomIdOrAlias: String) { + val permalink = session.permalinkService().createPermalink(roomIdOrAlias) + permalinkHandler + .launch(requireContext(), permalink, object : NavigationInterceptor { + override fun navToRoom(roomId: String?, eventId: String?): Boolean { + requireActivity().finish() + return false + } + }) + .subscribe { isSuccessful -> + if (!isSuccessful) { + requireContext().toast(R.string.room_error_not_found) + } + } + .disposeOnDestroyView() + } + override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) { Timber.v("PublicRoomClicked: $publicRoom") withState(viewModel) { state -> diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt new file mode 100644 index 0000000000..cf0ad811a2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomdirectory + +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +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_unknown_room) +abstract class UnknownRoomItem : VectorEpoxyModel() { + + @EpoxyAttribute + lateinit var avatarRenderer: AvatarRenderer + + @EpoxyAttribute + lateinit var matrixItem: MatrixItem + + @EpoxyAttribute + var identifier: String? = null + + @EpoxyAttribute + var globalListener: (() -> Unit)? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.rootView.setOnClickListener { globalListener?.invoke() } + avatarRenderer.render(matrixItem, holder.avatarView) + holder.nameView.text = matrixItem.displayName + } + + class Holder : VectorEpoxyHolder() { + val rootView by bind(R.id.itemUnknownRoomLayout) + val avatarView by bind(R.id.itemUnknownRoomAvatar) + val nameView by bind(R.id.itemUnknownRoomName) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index 9bf5646a99..d304594474 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -56,18 +56,17 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { } } - fun getIntent(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData): Intent { - return Intent(context, RoomPreviewActivity::class.java).apply { - putExtra(ARG, RoomPreviewData( - roomId = publicRoom.roomId, - roomName = publicRoom.name, - roomAlias = publicRoom.getPrimaryAlias(), - topic = publicRoom.topic, - worldReadable = publicRoom.worldReadable, - avatarUrl = publicRoom.avatarUrl, - homeServer = roomDirectoryData.homeServer - )) - } + fun newIntent(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData): Intent { + val roomPreviewData = RoomPreviewData( + roomId = publicRoom.roomId, + roomName = publicRoom.name, + roomAlias = publicRoom.getPrimaryAlias(), + topic = publicRoom.topic, + worldReadable = publicRoom.worldReadable, + avatarUrl = publicRoom.avatarUrl, + homeServer = roomDirectoryData.homeServer + ) + return newIntent(context, roomPreviewData) } } diff --git a/vector/src/main/res/layout/item_unknown_room.xml b/vector/src/main/res/layout/item_unknown_room.xml new file mode 100644 index 0000000000..a097767e63 --- /dev/null +++ b/vector/src/main/res/layout/item_unknown_room.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + \ 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 0b9cbab57a..7506a4d502 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2539,4 +2539,6 @@ Enable PIN If you want to reset your PIN, tap Forgot PIN to logout and reset. Confirm PIN to disable PIN + Can\'t open a room where you are banned from. + Can\'t find this room. Make sure it exists.