diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index ff1154acc3..9b41cf52d8 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -556,7 +556,7 @@ class HomeActivity : return true } - override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { + override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean { if (roomId == null) return false MatrixToBottomSheet.withLink(deepLink.toString()) .show(supportFragmentManager, "HA#MatrixToBottomSheet") diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 1c37b26c60..7eeb6ae665 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -984,7 +984,12 @@ class TimelineFragment @Inject constructor( true } R.id.menu_thread_timeline_copy_link -> { - requireActivity().toast("menu_thread_timeline_copy_link") + getRootThreadEventId()?.let { + val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, it) + copyToClipboard(requireContext(), permalink, false) + showSnackWithMessage(getString(R.string.copied_to_clipboard)) + + } true } R.id.menu_thread_timeline_view_in_room -> { @@ -992,7 +997,10 @@ class TimelineFragment @Inject constructor( true } R.id.menu_thread_timeline_share -> { - requireActivity().toast("menu_thread_timeline_share") + getRootThreadEventId()?.let { + val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, it) + shareText(requireContext(), permalink) + } true } else -> super.onOptionsItemSelected(item) @@ -1649,20 +1657,36 @@ class TimelineFragment @Inject constructor( viewLifecycleOwner.lifecycleScope.launch { val isManaged = permalinkHandler .launch(requireActivity(), url, object : NavigationInterceptor { - override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { + override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean { // Same room? - if (roomId == timelineArgs.roomId) { - // Navigation to same room - if (eventId == null) { + if (roomId != timelineArgs.roomId) return false + // Navigation to same room + if (!isThreadTimeLine()) { + + if (rootThreadEventId != null) { + // Thread link, so PermalinkHandler will handle the navigation + return false + } + return if (eventId == null) { showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room)) + true } else { // Highlight and scroll to this event roomDetailViewModel.handle(RoomDetailAction.NavigateToEvent(eventId, true)) + true + } + } else { + return if (rootThreadEventId == getRootThreadEventId() && eventId == null) { + showSnackWithMessage(getString(R.string.navigate_to_thread_when_already_in_the_thread)) + true + } else if (rootThreadEventId == getRootThreadEventId() && eventId != null) { + // we are in the same thread + roomDetailViewModel.handle(RoomDetailAction.NavigateToEvent(eventId, true)) + true + } else { + false } - return true } - // Not handled - return false } override fun navToMemberProfile(userId: String, deepLink: Uri): Boolean { @@ -1816,7 +1840,6 @@ class TimelineFragment @Inject constructor( } } - override fun onAvatarClicked(informationData: MessageInformationData) { // roomDetailViewModel.handle(RoomDetailAction.RequestVerification(informationData.userId)) openRoomMemberProfile(informationData.senderId) @@ -1862,7 +1885,7 @@ class TimelineFragment @Inject constructor( viewLifecycleOwner.lifecycleScope.launchWhenResumed { permalinkHandler .launch(requireContext(), url, object : NavigationInterceptor { - override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { + override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean { requireActivity().finish() return false } diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt index db052a42d3..fb1a6006c4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt @@ -94,6 +94,7 @@ class ThreadsActivity : VectorBaseActivity(), ToolbarCon TimelineFragment::class.java, TimelineArgs( roomId = threadTimelineArgs.roomId, + eventId = getEventIdToNavigate(), threadTimelineArgs = threadTimelineArgs )) @@ -141,15 +142,23 @@ class ThreadsActivity : VectorBaseActivity(), ToolbarCon private fun getThreadTimelineArgs(): ThreadTimelineArgs? = intent?.extras?.getParcelable(THREAD_TIMELINE_ARGS) private fun getThreadListArgs(): ThreadListArgs? = intent?.extras?.getParcelable(THREAD_LIST_ARGS) + private fun getEventIdToNavigate(): String? = intent?.extras?.getString(THREAD_EVENT_ID_TO_NAVIGATE) companion object { // private val FRAGMENT_TAG = RoomThreadDetailFragment::class.simpleName const val THREAD_TIMELINE_ARGS = "THREAD_TIMELINE_ARGS" + const val THREAD_EVENT_ID_TO_NAVIGATE = "THREAD_EVENT_ID_TO_NAVIGATE" const val THREAD_LIST_ARGS = "THREAD_LIST_ARGS" - fun newIntent(context: Context, threadTimelineArgs: ThreadTimelineArgs?, threadListArgs: ThreadListArgs?): Intent { + fun newIntent( + context: Context, + threadTimelineArgs: ThreadTimelineArgs?, + threadListArgs: ThreadListArgs?, + eventIdToNavigate: String? = null + ): Intent { return Intent(context, ThreadsActivity::class.java).apply { putExtra(THREAD_TIMELINE_ARGS, threadTimelineArgs) + putExtra(THREAD_EVENT_ID_TO_NAVIGATE, eventIdToNavigate) putExtra(THREAD_LIST_ARGS, threadListArgs) } 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 5f4a2168e9..3782a53d12 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 @@ -116,7 +116,12 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } - override fun openRoom(context: Context, roomId: String, eventId: String?, buildTask: Boolean) { + override fun openRoom( + context: Context, + roomId: String, + eventId: String?, + buildTask: Boolean + ) { if (sessionHolder.getSafeActiveSession()?.getRoom(roomId) == null) { fatalError("Trying to open an unknown room $roomId", vectorPreferences.failFast()) return @@ -511,16 +516,19 @@ class DefaultNavigator @Inject constructor( } } - override fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs) { + override fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs, eventIdToNavigate: String?) { context.startActivity(ThreadsActivity.newIntent( context = context, - threadTimelineArgs = threadTimelineArgs, - threadListArgs =null)) + threadTimelineArgs = threadTimelineArgs, + threadListArgs = null, + eventIdToNavigate = eventIdToNavigate + )) } + override fun openThreadList(context: Context, threadTimelineArgs: ThreadTimelineArgs) { context.startActivity(ThreadsActivity.newIntent( context = context, - threadTimelineArgs = null, + threadTimelineArgs = null, threadListArgs = ThreadListArgs( roomId = threadTimelineArgs.roomId, displayName = threadTimelineArgs.displayName, 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 37783d022b..d3a1a9eb03 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 @@ -142,7 +142,7 @@ interface Navigator { fun openCallTransfer(context: Context, callId: String) - fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs) + fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs, eventIdToNavigate: String? = null) fun openThreadList(context: Context, threadTimelineArgs: ThreadTimelineArgs) 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 a02cfe7517..614a95af3b 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 @@ -21,12 +21,14 @@ 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.home.room.threads.arguments.ThreadTimelineArgs import im.vector.app.features.navigation.Navigator import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkParser import org.matrix.android.sdk.api.session.permalinks.PermalinkService @@ -74,13 +76,27 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti return when (permalinkData) { is PermalinkData.RoomLink -> { val roomId = permalinkData.getRoomId() - if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId, rawLink) != true) { + val session = activeSessionHolder.getSafeActiveSession() + + val rootThreadEventId = permalinkData.eventId?.let { eventId -> + val room = roomId?.let { session?.getRoom(it) } + // Root thread will be opened in timeline +// if(room?.getTimeLineEvent(eventId)?.root?.threadDetails?.isRootThread == true){ +// room.getTimeLineEvent(eventId)?.root?.eventId +// }else{ + room?.getTimeLineEvent(eventId)?.root?.getRootThreadEventId() +// } + + } + + if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId, rawLink, rootThreadEventId) != true) { openRoom( context = context, roomId = roomId, permalinkData = permalinkData, rawLink = rawLink, - buildTask = buildTask + buildTask = buildTask, + rootThreadEventId = rootThreadEventId ) } true @@ -115,8 +131,8 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti private fun isPermalinkSupported(context: Context, url: String): Boolean { return url.startsWith(PermalinkService.MATRIX_TO_URL_BASE) || context.resources.getStringArray(R.array.permalink_supported_hosts).any { - url.startsWith(it) - } + url.startsWith(it) + } } private suspend fun PermalinkData.RoomLink.getRoomId(): String? { @@ -145,7 +161,8 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti roomId: String?, permalinkData: PermalinkData.RoomLink, rawLink: Uri, - buildTask: Boolean + buildTask: Boolean, + rootThreadEventId: String? =null ) { val session = activeSessionHolder.getSafeActiveSession() ?: return if (roomId == null) { @@ -155,6 +172,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti val roomSummary = session.getRoomSummary(roomId) val membership = roomSummary?.membership val eventId = permalinkData.eventId + // val roomAlias = permalinkData.getRoomAliasOrNull() val isSpace = roomSummary?.roomType == RoomType.SPACE return when { @@ -162,7 +180,10 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti membership?.isActive().orFalse() -> { if (!isSpace && membership == Membership.JOIN) { // If it's a room you're in, let's just open it, you can tap back if needed - navigator.openRoom(context, roomId, eventId, buildTask) + rootThreadEventId?.let { + val threadTimelineArgs = ThreadTimelineArgs(roomId, displayName = roomSummary.displayName, roomSummary.avatarUrl, it) + navigator.openThread(context, threadTimelineArgs, eventId) + } ?: navigator.openRoom(context, roomId, eventId, buildTask) } else { // maybe open space preview navigator.openSpacePreview(context, roomId)? if already joined? navigator.openMatrixToBottomSheet(context, rawLink.toString()) @@ -187,7 +208,7 @@ interface NavigationInterceptor { /** * Return true if the navigation has been intercepted */ - fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri? = null): Boolean { + fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri? = null, rootThreadEventId: String? = null): Boolean { return false } 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 b61583df55..fcc1d5fbf9 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 @@ -130,7 +130,7 @@ class PublicRoomsFragment @Inject constructor( val permalink = session.permalinkService().createPermalink(roomIdOrAlias) val isHandled = permalinkHandler .launch(requireContext(), permalink, object : NavigationInterceptor { - override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { + override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean { requireActivity().finish() return false } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index ea7ce4cf84..e9052ace75 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2259,6 +2259,7 @@ Matrix SDK Version Other third party notices You are already viewing this room! + You are already viewing this thread! Quick Reactions