diff --git a/vector/src/debug/res/layout/fragment_thread_list.xml b/vector/src/debug/res/layout/fragment_thread_list.xml
new file mode 100644
index 0000000000..cf3a79e776
--- /dev/null
+++ b/vector/src/debug/res/layout/fragment_thread_list.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index 2b7b445ad5..f0e68e8446 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -179,8 +179,7 @@
-
-
+
(), CallContro
private fun returnToChat() {
val roomId = withState(callViewModel) { it.roomId }
- val args = RoomDetailArgs(roomId)
+ val args = TimelineArgs(roomId)
val intent = RoomDetailActivity.newIntent(this, args).apply {
flags = FLAG_ACTIVITY_CLEAR_TOP
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
index 6c009d3786..ea0fb3f0ca 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
@@ -21,7 +21,7 @@ import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailActivity
-import im.vector.app.features.home.room.detail.RoomDetailArgs
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert
import org.matrix.android.sdk.api.session.Session
@@ -142,7 +142,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
R.drawable.ic_shield_black,
shouldBeDisplayedIn = { activity ->
if (activity is RoomDetailActivity) {
- activity.intent?.extras?.getParcelable(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
+ activity.intent?.extras?.getParcelable(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
it.roomId != pr.roomId
} ?: true
} else true
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
index 76c3816ce6..d9a10d8745 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
@@ -34,6 +34,7 @@ import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityRoomDetailBinding
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.matrixto.MatrixToBottomSheet
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.room.RequireActiveMembershipAction
@@ -102,16 +103,16 @@ class RoomDetailActivity :
super.onCreate(savedInstanceState)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
waitingView = views.waitingView.waitingView
- val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
- RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!)
+ val timelineArgs: TimelineArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
+ TimelineArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!)
} else {
intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
}
- if (roomDetailArgs == null) return
- currentRoomId = roomDetailArgs.roomId
+ if (timelineArgs == null) return
+ currentRoomId = timelineArgs.roomId
if (isFirstCreation()) {
- replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, roomDetailArgs)
+ replaceFragment(R.id.roomDetailContainer, TimelineFragment::class.java, timelineArgs)
replaceFragment(R.id.roomDetailDrawerContainer, BreadcrumbsFragment::class.java)
}
@@ -147,7 +148,7 @@ class RoomDetailActivity :
if (currentRoomId != switchToRoom.roomId) {
currentRoomId = switchToRoom.roomId
requireActiveMembershipViewModel.handle(RequireActiveMembershipAction.ChangeRoom(switchToRoom.roomId))
- replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, RoomDetailArgs(switchToRoom.roomId))
+ replaceFragment(R.id.roomDetailContainer, TimelineFragment::class.java, TimelineArgs(switchToRoom.roomId))
}
}
@@ -191,9 +192,9 @@ class RoomDetailActivity :
const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID"
const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT"
- fun newIntent(context: Context, roomDetailArgs: RoomDetailArgs): Intent {
+ fun newIntent(context: Context, timelineArgs: TimelineArgs): Intent {
return Intent(context, RoomDetailActivity::class.java).apply {
- putExtra(EXTRA_ROOM_DETAIL_ARGS, roomDetailArgs)
+ putExtra(EXTRA_ROOM_DETAIL_ARGS, timelineArgs)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index ca7ed4b6ec..5854d35fb6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -153,7 +153,7 @@ class RoomDetailViewModel @AssistedInject constructor(
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? {
- val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
+ val fragment: TimelineFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.roomDetailViewModelFactory.create(state)
}
@@ -668,20 +668,30 @@ class RoomDetailViewModel @AssistedInject constructor(
private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled()
fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state ->
+
if (state.asyncRoomSummary()?.membership != Membership.JOIN) {
return@withState false
}
- when (itemId) {
- R.id.timeline_setting -> true
- R.id.invite -> state.canInvite
- R.id.open_matrix_apps -> true
- R.id.voice_call -> state.isWebRTCCallOptionAvailable()
- R.id.video_call -> state.isWebRTCCallOptionAvailable() || state.jitsiState.confId == null || state.jitsiState.hasJoined
- // Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^
- R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
- R.id.search -> true
- R.id.dev_tools -> vectorPreferences.developerMode()
- else -> false
+
+ if (initialState.isThreadTimeline()) {
+ when (itemId) {
+ R.id.menu_thread_timeline_more -> true
+ else -> false
+ }
+ } else {
+ when (itemId) {
+ R.id.timeline_setting -> true
+ R.id.invite -> state.canInvite
+ R.id.open_matrix_apps -> true
+ R.id.voice_call -> state.isWebRTCCallOptionAvailable()
+ R.id.video_call -> state.isWebRTCCallOptionAvailable() || state.jitsiState.confId == null || state.jitsiState.hasJoined
+ // Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^
+ R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
+ R.id.search -> true
+ R.id.threads -> true
+ R.id.dev_tools -> vectorPreferences.developerMode()
+ else -> false
+ }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
index 1848f1b28e..fa772ca073 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
@@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
@@ -69,12 +70,12 @@ data class RoomDetailViewState(
val rootThreadEventId: String? = null
) : MavericksState {
- constructor(args: RoomDetailArgs) : this(
+ constructor(args: TimelineArgs) : this(
roomId = args.roomId,
eventId = args.eventId,
// Also highlight the target event, if any
highlightedEventId = args.eventId,
- rootThreadEventId = args.roomThreadDetailArgs?.eventId
+ rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId
)
fun isWebRTCCallOptionAvailable() = (asyncRoomSummary.invoke()?.joinedMembersCount ?: 0) <= 2
@@ -84,4 +85,7 @@ data class RoomDetailViewState(
fun hasActiveJitsiWidget() = activeRoomWidgets()?.any { it.type == WidgetType.Jitsi && it.isActive }.orFalse()
fun isDm() = asyncRoomSummary()?.isDirect == true
+
+ fun isThreadTimeline() = rootThreadEventId != null
+
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
similarity index 93%
rename from vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
rename to vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
index 6dfe29cec6..9afd5a2fc9 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
@@ -25,7 +25,6 @@ import android.graphics.Typeface
import android.net.Uri
import android.os.Build
import android.os.Bundle
-import android.os.Parcelable
import android.text.Spannable
import android.text.format.DateUtils
import android.view.HapticFeedbackConstants
@@ -49,7 +48,6 @@ import androidx.core.text.toSpannable
import androidx.core.util.Pair
import androidx.core.view.ViewCompat
import androidx.core.view.forEach
-import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.fragment.app.setFragmentResultListener
@@ -134,6 +132,7 @@ import im.vector.app.features.command.Command
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.home.AvatarRenderer
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.composer.SendMode
import im.vector.app.features.home.room.detail.composer.TextComposerAction
import im.vector.app.features.home.room.detail.composer.TextComposerView
@@ -162,8 +161,7 @@ import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
import im.vector.app.features.home.room.detail.views.RoomDetailLazyLoadedViews
import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
-import im.vector.app.features.home.room.threads.detail.arguments.RoomThreadDetailArgs
-import im.vector.app.features.home.room.threads.detail.RoomThreadDetailActivity
+import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.PillImageSpan
import im.vector.app.features.html.PillsPostProcessor
@@ -188,7 +186,6 @@ import im.vector.app.features.widgets.WidgetArgs
import im.vector.app.features.widgets.WidgetKind
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import kotlinx.coroutines.launch
-import kotlinx.parcelize.Parcelize
import nl.dionsegijn.konfetti.models.Shape
import nl.dionsegijn.konfetti.models.Size
import org.billcarsonfr.jsonviewer.JSonViewerDialog
@@ -224,16 +221,7 @@ import java.util.UUID
import java.util.concurrent.TimeUnit
import javax.inject.Inject
-@Parcelize
-data class RoomDetailArgs(
- val roomId: String,
- val eventId: String? = null,
- val sharedData: SharedData? = null,
- val openShareSpaceForId: String? = null,
- val roomThreadDetailArgs: RoomThreadDetailArgs? = null
-) : Parcelable
-
-class RoomDetailFragment @Inject constructor(
+class TimelineFragment @Inject constructor(
private val session: Session,
private val avatarRenderer: AvatarRenderer,
private val timelineEventController: TimelineEventController,
@@ -282,16 +270,16 @@ class RoomDetailFragment @Inject constructor(
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
- private val roomDetailArgs: RoomDetailArgs by args()
+ private val timelineArgs: TimelineArgs by args()
private val glideRequests by lazy {
GlideApp.with(this)
}
private val pillsPostProcessor by lazy {
- pillsPostProcessorFactory.create(roomDetailArgs.roomId)
+ pillsPostProcessorFactory.create(timelineArgs.roomId)
}
private val autoCompleter: AutoCompleter by lazy {
- autoCompleterFactory.create(roomDetailArgs.roomId, isThreadTimeLine())
+ autoCompleterFactory.create(timelineArgs.roomId, isThreadTimeLine())
}
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
@@ -308,6 +296,8 @@ class RoomDetailFragment @Inject constructor(
override fun getMenuRes() = R.menu.menu_timeline
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
+ private lateinit var sharedActivityActionViewModel: RoomDetailSharedActionViewModel
+
private lateinit var knownCallsViewModel: SharedKnownCallsViewModel
private lateinit var layoutManager: LinearLayoutManager
@@ -341,10 +331,11 @@ class RoomDetailFragment @Inject constructor(
lifecycle.addObserver(ConferenceEventObserver(vectorBaseActivity, this::onBroadcastJitsiEvent))
super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
+ sharedActivityActionViewModel = activityViewModelProvider.get(RoomDetailSharedActionViewModel::class.java)
knownCallsViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java)
attachmentsHelper = AttachmentsHelper(requireContext(), this).register()
callActionsHandler = StartCallActionsHandler(
- roomId = roomDetailArgs.roomId,
+ roomId = timelineArgs.roomId,
fragment = this,
vectorPreferences = vectorPreferences,
roomDetailViewModel = roomDetailViewModel,
@@ -355,11 +346,7 @@ class RoomDetailFragment @Inject constructor(
)
keyboardStateUtils = KeyboardStateUtils(requireActivity())
lazyLoadedViews.bind(views)
- if (isThreadTimeLine()) {
- views.roomToolbar.isGone = true
- } else {
- setupToolbar(views.roomToolbar)
- }
+ setupToolbar(views.roomToolbar)
setupRecyclerView()
setupComposer()
setupNotificationView()
@@ -370,8 +357,8 @@ class RoomDetailFragment @Inject constructor(
setupRemoveJitsiWidgetView()
setupVoiceMessageView()
- views.roomToolbarContentView.debouncedClicks {
- navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
+ views.includeRoomToolbar.roomToolbarContentView.debouncedClicks {
+ navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
}
sharedActionViewModel
@@ -456,7 +443,7 @@ class RoomDetailFragment @Inject constructor(
RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView()
is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it)
- RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId)
+ RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), timelineArgs.roomId)
RoomDetailViewEvents.OpenSetRoomAvatarDialog -> galleryOrCameraDialogHelper.show()
RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings()
is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item ->
@@ -532,7 +519,7 @@ class RoomDetailFragment @Inject constructor(
private fun handleShowRoomUpgradeDialog(roomDetailViewEvents: TextComposerViewEvents.ShowRoomUpgradeDialog) {
val tag = MigrateRoomBottomSheet::javaClass.name
- MigrateRoomBottomSheet.newInstance(roomDetailArgs.roomId, roomDetailViewEvents.newVersion)
+ MigrateRoomBottomSheet.newInstance(timelineArgs.roomId, roomDetailViewEvents.newVersion)
.show(parentFragmentManager, tag)
}
@@ -578,7 +565,7 @@ class RoomDetailFragment @Inject constructor(
private fun handleOpenRoomSettings() {
navigator.openRoomProfile(
requireContext(),
- roomDetailArgs.roomId,
+ timelineArgs.roomId,
RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS
)
}
@@ -600,7 +587,7 @@ class RoomDetailFragment @Inject constructor(
WidgetArgs(
baseUrl = it.domain,
kind = WidgetKind.ROOM,
- roomId = roomDetailArgs.roomId,
+ roomId = timelineArgs.roomId,
widgetId = it.widget.widgetId
)
).apply {
@@ -626,7 +613,7 @@ class RoomDetailFragment @Inject constructor(
navigator.openIntegrationManager(
context = requireContext(),
activityResultLauncher = integrationManagerActivityResultLauncher,
- roomId = roomDetailArgs.roomId,
+ roomId = timelineArgs.roomId,
integId = null,
screen = screen
)
@@ -717,11 +704,11 @@ class RoomDetailFragment @Inject constructor(
}
private fun joinJitsiRoom(jitsiWidget: Widget, enableVideo: Boolean) {
- navigator.openRoomWidget(requireContext(), roomDetailArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo))
+ navigator.openRoomWidget(requireContext(), timelineArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo))
}
private fun openStickerPicker(event: RoomDetailViewEvents.OpenStickerPicker) {
- navigator.openStickerPicker(requireContext(), stickerActivityResultLauncher, roomDetailArgs.roomId, event.widget)
+ navigator.openStickerPicker(requireContext(), stickerActivityResultLauncher, timelineArgs.roomId, event.widget)
}
private fun startOpenFileIntent(action: RoomDetailViewEvents.OpenFile) {
@@ -795,7 +782,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun handleShareData() {
- when (val sharedData = roomDetailArgs.sharedData) {
+ when (val sharedData = timelineArgs.sharedData) {
is SharedData.Text -> {
textComposerViewModel.handle(TextComposerAction.EnterRegularMode(sharedData.text, fromSharing = true))
}
@@ -808,7 +795,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun handleSpaceShare() {
- roomDetailArgs.openShareSpaceForId?.let { spaceId ->
+ timelineArgs.openShareSpaceForId?.let { spaceId ->
ShareSpaceBottomSheet.show(childFragmentManager, spaceId, true)
view?.post {
handleChatEffect(ChatEffect.CONFETTI)
@@ -909,7 +896,6 @@ class RoomDetailFragment @Inject constructor(
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
- if (isThreadTimeLine()) return
// We use a custom layout for this menu item, so we need to set a ClickListener
menu.findItem(R.id.open_matrix_apps)?.let { menuItem ->
menuItem.actionView.setOnClickListener {
@@ -923,15 +909,11 @@ class RoomDetailFragment @Inject constructor(
}
override fun onPrepareOptionsMenu(menu: Menu) {
- if (isThreadTimeLine()) {
- menu.forEach {
- it.isVisible = false
- }
- return
- }
+
menu.forEach {
it.isVisible = roomDetailViewModel.isMenuItemVisible(it.itemId)
}
+
withState(roomDetailViewModel) { state ->
// Set the visual state of the call buttons (voice/video) to enabled/disabled according to user permissions
val hasCallInRoom = callManager.getCallsByRoomId(state.roomId).isNotEmpty() || state.jitsiState.hasJoined
@@ -968,41 +950,72 @@ class RoomDetailFragment @Inject constructor(
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
- R.id.invite -> {
- navigator.openInviteUsersToRoom(requireActivity(), roomDetailArgs.roomId)
+ R.id.invite -> {
+ navigator.openInviteUsersToRoom(requireActivity(), timelineArgs.roomId)
true
}
- R.id.timeline_setting -> {
- navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
+ R.id.timeline_setting -> {
+ navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
true
}
- R.id.open_matrix_apps -> {
+ R.id.open_matrix_apps -> {
roomDetailViewModel.handle(RoomDetailAction.ManageIntegrations)
true
}
- R.id.voice_call -> {
+ R.id.voice_call -> {
callActionsHandler.onVoiceCallClicked()
true
}
- R.id.video_call -> {
+ R.id.video_call -> {
callActionsHandler.onVideoCallClicked()
true
}
- R.id.search -> {
+ R.id.threads -> {
+ requireActivity().toast("View All Threads")
+ true
+ }
+ R.id.search -> {
handleSearchAction()
true
}
- R.id.dev_tools -> {
- navigator.openDevTools(requireContext(), roomDetailArgs.roomId)
+ R.id.dev_tools -> {
+ navigator.openDevTools(requireContext(), timelineArgs.roomId)
true
}
- else -> super.onOptionsItemSelected(item)
+ R.id.menu_thread_timeline_copy_link -> {
+ requireActivity().toast("menu_thread_timeline_copy_link")
+ true
+ }
+ R.id.menu_thread_timeline_view_in_room -> {
+ handleViewInRoomAction()
+ true
+ }
+ R.id.menu_thread_timeline_share -> {
+ requireActivity().toast("menu_thread_timeline_share")
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+
+ /**
+ * View and highlight the original root thread message in the main timeline
+ */
+ private fun handleViewInRoomAction(){
+ getRootThreadEventId()?.let {
+ val newRoom = timelineArgs.copy(threadTimelineArgs = null,eventId = it)
+ context?.let{ con ->
+ val int = RoomDetailActivity.newIntent(con, newRoom)
+ int.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
+ con.startActivity(int)
+
+ }
}
}
private fun handleSearchAction() {
- if (session.getRoom(roomDetailArgs.roomId)?.isEncrypted() == false) {
- navigator.openSearch(requireContext(), roomDetailArgs.roomId)
+ if (session.getRoom(timelineArgs.roomId)?.isEncrypted() == false) {
+ navigator.openSearch(requireContext(), timelineArgs.roomId)
} else {
showDialogWithMessage(getString(R.string.search_is_not_supported_in_e2e_room))
}
@@ -1080,7 +1093,7 @@ class RoomDetailFragment @Inject constructor(
override fun onResume() {
super.onResume()
- notificationDrawerManager.setCurrentRoom(roomDetailArgs.roomId)
+ notificationDrawerManager.setCurrentRoom(timelineArgs.roomId)
roomDetailPendingActionStore.data?.let { handlePendingAction(it) }
roomDetailPendingActionStore.data = null
@@ -1322,7 +1335,7 @@ class RoomDetailFragment @Inject constructor(
views.composerLayout.callback = object : TextComposerView.Callback {
override fun onAddAttachment() {
if (!::attachmentTypeSelector.isInitialized) {
- attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@RoomDetailFragment)
+ attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@TimelineFragment)
}
attachmentTypeSelector.show(views.composerLayout.views.attachmentButton, keyboardStateUtils.isKeyboardShowing)
}
@@ -1420,7 +1433,7 @@ class RoomDetailFragment @Inject constructor(
} else if (summary?.membership == Membership.INVITE && inviter != null) {
views.hideComposerViews()
lazyLoadedViews.inviteView(true)?.apply {
- callback = this@RoomDetailFragment
+ callback = this@TimelineFragment
isVisible = true
render(inviter, VectorInviteView.Mode.LARGE, mainState.changeMembershipState)
setOnClickListener { }
@@ -1437,23 +1450,35 @@ class RoomDetailFragment @Inject constructor(
}
private fun renderToolbar(roomSummary: RoomSummary?, typingMessage: String?) {
- if (roomSummary == null) {
- views.roomToolbarContentView.isClickable = false
+ if (!isThreadTimeLine()) {
+ views.includeRoomToolbar.roomToolbarContentView.isVisible = true
+ views.includeThreadToolbar.roomToolbarThreadConstraintLayout.isVisible = false
+ if (roomSummary == null) {
+ views.includeRoomToolbar.roomToolbarContentView.isClickable = false
+ } else {
+ views.includeRoomToolbar.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
+ views.includeRoomToolbar.roomToolbarTitleView.text = roomSummary.displayName
+ avatarRenderer.render(roomSummary.toMatrixItem(), views.includeRoomToolbar.roomToolbarAvatarImageView)
+ renderSubTitle(typingMessage, roomSummary.topic)
+ views.includeRoomToolbar.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
+ views.includeRoomToolbar.roomToolbarPresenceImageView.render(roomSummary.isDirect, roomSummary.directUserPresence)
+ views.includeRoomToolbar.roomToolbarPublicImageView.isVisible = roomSummary.isPublic && !roomSummary.isDirect
+ }
} else {
- views.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
- views.roomToolbarTitleView.text = roomSummary.displayName
- avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView)
- renderSubTitle(typingMessage, roomSummary.topic)
- views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
- views.roomToolbarPresenceImageView.render(roomSummary.isDirect, roomSummary.directUserPresence)
- views.roomToolbarPublicImageView.isVisible = roomSummary.isPublic && !roomSummary.isDirect
+ views.includeRoomToolbar.roomToolbarContentView.isVisible = false
+ views.includeThreadToolbar.roomToolbarThreadConstraintLayout.isVisible = true
+ timelineArgs.threadTimelineArgs?.let {
+ val matrixItem = MatrixItem.RoomItem(it.roomId, it.displayName, it.avatarUrl)
+ avatarRenderer.render(matrixItem, views.includeThreadToolbar.roomToolbarThreadImageView)
+ views.includeThreadToolbar.roomToolbarThreadSubtitleTextView.text = it.displayName
+ }
}
}
private fun renderSubTitle(typingMessage: String?, topic: String) {
// TODO Temporary place to put typing data
val subtitle = typingMessage?.takeIf { it.isNotBlank() } ?: topic
- views.roomToolbarSubtitleView.apply {
+ views.includeRoomToolbar.roomToolbarSubtitleView.apply {
setTextOrHide(subtitle)
if (typingMessage.isNullOrBlank()) {
setTextColor(colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
@@ -1592,14 +1617,14 @@ class RoomDetailFragment @Inject constructor(
is RoomDetailAction.RequestVerification -> {
Timber.v("## SAS RequestVerification action")
VerificationBottomSheet.withArgs(
- roomDetailArgs.roomId,
+ timelineArgs.roomId,
data.userId
).show(parentFragmentManager, "REQ")
}
is RoomDetailAction.AcceptVerificationRequest -> {
Timber.v("## SAS AcceptVerificationRequest action")
VerificationBottomSheet.withArgs(
- roomDetailArgs.roomId,
+ timelineArgs.roomId,
data.otherUserId,
data.transactionId
).show(parentFragmentManager, "REQ")
@@ -1609,7 +1634,7 @@ class RoomDetailFragment @Inject constructor(
VerificationBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(Mavericks.KEY_ARG, VerificationBottomSheet.VerificationArgs(
- otherUserId, data.transactionId, roomId = roomDetailArgs.roomId))
+ otherUserId, data.transactionId, roomId = timelineArgs.roomId))
}
}.show(parentFragmentManager, "REQ")
}
@@ -1624,7 +1649,7 @@ class RoomDetailFragment @Inject constructor(
.launch(requireActivity(), url, object : NavigationInterceptor {
override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean {
// Same room?
- if (roomId == roomDetailArgs.roomId) {
+ if (roomId == timelineArgs.roomId) {
// Navigation to same room
if (eventId == null) {
showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room))
@@ -1691,7 +1716,7 @@ class RoomDetailFragment @Inject constructor(
override fun onImageMessageClicked(messageImageContent: MessageImageInfoContent, mediaData: ImageContentRenderer.Data, view: View) {
navigator.openMediaViewer(
activity = requireActivity(),
- roomId = roomDetailArgs.roomId,
+ roomId = timelineArgs.roomId,
mediaData = mediaData,
view = view
) { pairs ->
@@ -1703,7 +1728,7 @@ class RoomDetailFragment @Inject constructor(
override fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View) {
navigator.openMediaViewer(
activity = requireActivity(),
- roomId = roomDetailArgs.roomId,
+ roomId = timelineArgs.roomId,
mediaData = mediaData,
view = view
) { pairs ->
@@ -1738,7 +1763,7 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.LoadMoreTimelineEvents(direction))
}
- override fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View) {
+ override fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View, isRootThreadEvent: Boolean) {
when (messageContent) {
is MessageVerificationRequestContent -> {
roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, null))
@@ -1751,11 +1776,14 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.TapOnFailedToDecrypt(informationData.eventId))
}
}
+ if (isRootThreadEvent) {
+ navigateToThreadTimeline(informationData.eventId)
+ }
}
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean {
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
- val roomId = roomDetailArgs.roomId
+ val roomId = timelineArgs.roomId
this.view?.hideKeyboard()
MessageActionsBottomSheet
@@ -1786,7 +1814,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun openRoomMemberProfile(userId: String) {
- navigator.openRoomMemberProfile(userId = userId, roomId = roomDetailArgs.roomId, context = requireActivity())
+ navigator.openRoomMemberProfile(userId = userId, roomId = timelineArgs.roomId, context = requireActivity())
}
override fun onMemberNameClicked(informationData: MessageInformationData) {
@@ -1804,12 +1832,12 @@ class RoomDetailFragment @Inject constructor(
}
override fun onLongClickOnReactionPill(informationData: MessageInformationData, reaction: String) {
- ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, informationData)
+ ViewReactionsBottomSheet.newInstance(timelineArgs.roomId, informationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
}
override fun onEditedDecorationClicked(informationData: MessageInformationData) {
- ViewEditHistoryBottomSheet.newInstance(roomDetailArgs.roomId, informationData)
+ ViewEditHistoryBottomSheet.newInstance(timelineArgs.roomId, informationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_EDITS")
}
@@ -1921,7 +1949,7 @@ class RoomDetailFragment @Inject constructor(
emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId))
}
is EventSharedAction.ViewReactions -> {
- ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData)
+ ViewReactionsBottomSheet.newInstance(timelineArgs.roomId, action.messageInformationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
}
is EventSharedAction.Copy -> {
@@ -1978,20 +2006,13 @@ class RoomDetailFragment @Inject constructor(
}
is EventSharedAction.ReplyInThread -> {
if (!views.voiceMessageRecorderView.isActive()) {
- context?.let {
- val roomThreadDetailArgs = RoomThreadDetailArgs(
- roomId = roomDetailArgs.roomId,
- displayName = roomDetailViewModel.getRoomSummary()?.displayName,
- avatarUrl = roomDetailViewModel.getRoomSummary()?.avatarUrl,
- eventId = action.eventId)
- startActivity(RoomThreadDetailActivity.newIntent(it, roomThreadDetailArgs))
- }
+ navigateToThreadTimeline(action.eventId)
} else {
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
}
}
is EventSharedAction.CopyPermalink -> {
- val permalink = session.permalinkService().createPermalink(roomDetailArgs.roomId, action.eventId)
+ val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, action.eventId)
copyToClipboard(requireContext(), permalink, false)
showSnackWithMessage(getString(R.string.copied_to_clipboard))
}
@@ -2114,15 +2135,31 @@ class RoomDetailFragment @Inject constructor(
.show()
}
+ /**
+ * Navigate to Threads timeline for the specified threadRootEventId
+ * using the RoomThreadDetailActivity
+ */
+
+ private fun navigateToThreadTimeline(rootThreadEventId: String) {
+ context?.let {
+ val roomThreadDetailArgs = ThreadTimelineArgs(
+ roomId = timelineArgs.roomId,
+ displayName = roomDetailViewModel.getRoomSummary()?.displayName,
+ avatarUrl = roomDetailViewModel.getRoomSummary()?.avatarUrl,
+ rootThreadEventId = rootThreadEventId)
+ navigator.openThread(it, roomThreadDetailArgs)
+ }
+ }
+
// VectorInviteView.Callback
override fun onAcceptInvite() {
- notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
+ notificationDrawerManager.clearMemberShipNotificationForRoom(timelineArgs.roomId)
roomDetailViewModel.handle(RoomDetailAction.AcceptInvite)
}
override fun onRejectInvite() {
- notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
+ notificationDrawerManager.clearMemberShipNotificationForRoom(timelineArgs.roomId)
roomDetailViewModel.handle(RoomDetailAction.RejectInvite)
}
@@ -2221,6 +2258,13 @@ class RoomDetailFragment @Inject constructor(
}
}
- private fun isThreadTimeLine(): Boolean = roomDetailArgs.roomThreadDetailArgs != null
- fun getRootThreadEventId(): String? = roomDetailArgs.roomThreadDetailArgs?.eventId
+ /**
+ * Returns true if the current room is a Thread room, false otherwise
+ */
+ private fun isThreadTimeLine(): Boolean = timelineArgs.threadTimelineArgs?.rootThreadEventId != null
+
+ /**
+ * Returns the root thread event if we are in a thread room, otherwise returns null
+ */
+ fun getRootThreadEventId(): String? = timelineArgs.threadTimelineArgs?.rootThreadEventId
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/arguments/TimelineArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/arguments/TimelineArgs.kt
new file mode 100644
index 0000000000..26455e04c7
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/arguments/TimelineArgs.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.home.room.detail.arguments
+
+import android.os.Parcelable
+import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
+import im.vector.app.features.share.SharedData
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TimelineArgs(
+ val roomId: String,
+ val eventId: String? = null,
+ val sharedData: SharedData? = null,
+ val openShareSpaceForId: String? = null,
+ val threadTimelineArgs: ThreadTimelineArgs? = null
+) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt
index bcc26247a2..af870c3313 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt
@@ -29,7 +29,7 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.command.CommandParser
import im.vector.app.features.command.ParsedCommand
import im.vector.app.features.home.room.detail.ChatEffect
-import im.vector.app.features.home.room.detail.RoomDetailFragment
+import im.vector.app.features.home.room.detail.TimelineFragment
import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
import im.vector.app.features.home.room.detail.toMessageType
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
@@ -42,7 +42,6 @@ import org.commonmark.renderer.html.HtmlRenderer
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.EventType
-import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
@@ -772,7 +771,7 @@ class TextComposerViewModel @AssistedInject constructor(
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel {
- val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
+ val fragment: TimelineFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.textComposerViewModelFactory.create(state)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt
index f4dd5adebe..0e8d9e1e86 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt
@@ -17,7 +17,7 @@
package im.vector.app.features.home.room.detail.composer
import com.airbnb.mvrx.MavericksState
-import im.vector.app.features.home.room.detail.RoomDetailArgs
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
/**
@@ -53,9 +53,9 @@ data class TextComposerViewState(
val isComposerVisible: Boolean
get() = canSendMessage && !isVoiceRecording
- constructor(args: RoomDetailArgs) : this(
+ constructor(args: TimelineArgs) : this(
roomId = args.roomId,
- rootThreadEventId = args.roomThreadDetailArgs?.eventId)
+ rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId)
fun isInThreadTimeline(): Boolean = rootThreadEventId != null
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt
index d08259d739..11c90b3482 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt
@@ -140,7 +140,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
}
interface BaseCallback {
- fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View)
+ fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View, isRootThreadEvent: Boolean)
fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
index 11061cbc9a..a30a0b851e 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
@@ -43,7 +43,7 @@ class MessageItemAttributesFactory @Inject constructor(
callback?.onEventLongClicked(informationData, messageContent, view) ?: false
},
itemClickListener = { view ->
- callback?.onEventCellClicked(informationData, messageContent, view)
+ callback?.onEventCellClicked(informationData, messageContent, view, threadDetails?.isRootThread ?: false)
},
memberClickListener = {
callback?.onMemberNameClicked(informationData)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/RoomThreadsActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/RoomThreadsActivity.kt
deleted file mode 100644
index 0ad1d02ffb..0000000000
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/RoomThreadsActivity.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 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.home.room.threads
-
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import androidx.appcompat.widget.SearchView
-import im.vector.app.R
-import im.vector.app.core.di.ScreenComponent
-import im.vector.app.core.extensions.replaceFragment
-import im.vector.app.core.platform.VectorBaseActivity
-import im.vector.app.databinding.ActivityFilteredRoomsBinding
-import im.vector.app.databinding.ActivityRoomThreadsBinding
-import im.vector.app.features.home.RoomListDisplayMode
-import im.vector.app.features.home.room.list.RoomListFragment
-import im.vector.app.features.home.room.list.RoomListParams
-
-class RoomThreadsActivity : VectorBaseActivity() {
-
-// private val roomListFragment: RoomListFragment?
-// get() {
-// return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomListFragment
-// }
-
- override fun getBinding() = ActivityRoomThreadsBinding.inflate(layoutInflater)
-
- override fun getCoordinatorLayout() = views.coordinatorLayout
-
- override fun injectWith(injector: ScreenComponent) {
- injector.inject(this)
- }
-
- override fun getMenuRes() = R.menu.menu_room_threads
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- configureToolbar(views.roomThreadsToolbar)
-// if (isFirstCreation()) {
-// val params = RoomListParams(RoomListDisplayMode.FILTERED)
-// replaceFragment(R.id.filteredRoomsFragmentContainer, RoomListFragment::class.java, params, FRAGMENT_TAG)
-// }
- }
-
- companion object {
- private const val FRAGMENT_TAG = "RoomListFragment"
-
- fun newIntent(context: Context): Intent {
- return Intent(context, RoomThreadsActivity::class.java)
- }
- }
-}
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
new file mode 100644
index 0000000000..73da1354af
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt
@@ -0,0 +1,132 @@
+/*
+ * 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.home.room.threads
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import com.google.android.material.appbar.MaterialToolbar
+import im.vector.app.R
+import im.vector.app.core.di.ScreenComponent
+import im.vector.app.core.extensions.replaceFragment
+import im.vector.app.core.platform.ToolbarConfigurable
+import im.vector.app.core.platform.VectorBaseActivity
+import im.vector.app.core.platform.VectorViewModelAction
+import im.vector.app.databinding.ActivityThreadsBinding
+import im.vector.app.features.home.AvatarRenderer
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
+import im.vector.app.features.home.room.detail.TimelineFragment
+import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
+import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
+import im.vector.app.features.home.room.threads.detail.ThreadListFragment
+import javax.inject.Inject
+
+class ThreadsActivity : VectorBaseActivity(), ToolbarConfigurable {
+
+ @Inject
+ lateinit var avatarRenderer: AvatarRenderer
+
+// private val roomThreadDetailFragment: RoomThreadDetailFragment?
+// get() {
+// return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomThreadDetailFragment
+// }
+
+ override fun getBinding() = ActivityThreadsBinding.inflate(layoutInflater)
+
+ override fun getCoordinatorLayout() = views.coordinatorLayout
+
+ override fun injectWith(injector: ScreenComponent) {
+ injector.inject(this)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ initFragment()
+ }
+
+ private fun initFragment() {
+ if (isFirstCreation()) {
+ when (val fragment = fragmentToNavigate()) {
+ is DisplayFragment.ThreadList -> {
+ initThreadListFragment(fragment.threadListArgs)
+ }
+ is DisplayFragment.ThreadTimeLine -> {
+ initThreadTimelineFragment(fragment.threadTimelineArgs)
+ }
+ is DisplayFragment.ErrorFragment -> {
+ finish()
+ }
+ }
+ }
+ }
+
+ private fun initThreadListFragment(threadListArgs: ThreadListArgs) {
+ replaceFragment(
+ R.id.threadsActivityFragmentContainer,
+ ThreadListFragment::class.java,
+ threadListArgs)
+ }
+
+ private fun initThreadTimelineFragment(threadTimelineArgs: ThreadTimelineArgs) =
+ replaceFragment(
+ R.id.threadsActivityFragmentContainer,
+ TimelineFragment::class.java,
+ TimelineArgs(
+ roomId = threadTimelineArgs.roomId,
+ threadTimelineArgs = threadTimelineArgs
+ ))
+
+ override fun configure(toolbar: MaterialToolbar) {
+ configureToolbar(toolbar)
+ }
+
+ /**
+ * Determine in witch fragment we should navigate
+ */
+ private fun fragmentToNavigate(): DisplayFragment {
+ getThreadTimelineArgs()?.let {
+ return DisplayFragment.ThreadTimeLine(it)
+ }
+ getThreadListArgs()?.let {
+ return DisplayFragment.ThreadList(it)
+ }
+ return DisplayFragment.ErrorFragment
+ }
+
+ private fun getThreadTimelineArgs(): ThreadTimelineArgs? = intent?.extras?.getParcelable(THREAD_TIMELINE_ARGS)
+ private fun getThreadListArgs(): ThreadListArgs? = intent?.extras?.getParcelable(THREAD_LIST_ARGS)
+
+ companion object {
+ // private val FRAGMENT_TAG = RoomThreadDetailFragment::class.simpleName
+ const val THREAD_TIMELINE_ARGS = "THREAD_TIMELINE_ARGS"
+ const val THREAD_LIST_ARGS = "THREAD_LIST_ARGS"
+
+ fun newIntent(context: Context, threadTimelineArgs: ThreadTimelineArgs?, threadListArgs: ThreadListArgs?): Intent {
+ return Intent(context, ThreadsActivity::class.java).apply {
+ putExtra(THREAD_TIMELINE_ARGS, threadTimelineArgs)
+ putExtra(THREAD_LIST_ARGS, threadListArgs)
+
+ }
+ }
+ }
+
+ sealed class DisplayFragment {
+ data class ThreadList(val threadListArgs: ThreadListArgs) : DisplayFragment()
+ data class ThreadTimeLine(val threadTimelineArgs: ThreadTimelineArgs) : DisplayFragment()
+ object ErrorFragment : DisplayFragment()
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadListArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadListArgs.kt
new file mode 100644
index 0000000000..23b72e5f32
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadListArgs.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.home.room.threads.arguments
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class ThreadListArgs(
+ val roomId: String
+) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/detail/arguments/RoomThreadDetailArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt
similarity index 85%
rename from vector/src/main/java/im/vector/app/features/home/room/threads/detail/arguments/RoomThreadDetailArgs.kt
rename to vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt
index 5fce24cd0d..2ebed2f745 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/detail/arguments/RoomThreadDetailArgs.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package im.vector.app.features.home.room.threads.detail.arguments
+package im.vector.app.features.home.room.threads.arguments
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
-data class RoomThreadDetailArgs(
+data class ThreadTimelineArgs(
val roomId: String,
val displayName: String?,
val avatarUrl: String?,
- val eventId: String? = null,
+ val rootThreadEventId: String? = null
) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/detail/RoomThreadDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/detail/RoomThreadDetailActivity.kt
deleted file mode 100644
index c82fa353e4..0000000000
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/detail/RoomThreadDetailActivity.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 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.home.room.threads.detail
-
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import im.vector.app.R
-import im.vector.app.core.di.ScreenComponent
-import im.vector.app.core.extensions.replaceFragment
-import im.vector.app.core.platform.VectorBaseActivity
-import im.vector.app.databinding.ActivityRoomThreadDetailBinding
-import im.vector.app.features.home.AvatarRenderer
-import im.vector.app.features.home.room.detail.RoomDetailArgs
-import im.vector.app.features.home.room.detail.RoomDetailFragment
-import im.vector.app.features.home.room.threads.detail.arguments.RoomThreadDetailArgs
-import org.matrix.android.sdk.api.util.MatrixItem
-import javax.inject.Inject
-
-class RoomThreadDetailActivity : VectorBaseActivity() {
-
- @Inject
- lateinit var avatarRenderer: AvatarRenderer
-
-// private val roomThreadDetailFragment: RoomThreadDetailFragment?
-// get() {
-// return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomThreadDetailFragment
-// }
-
- override fun getBinding() = ActivityRoomThreadDetailBinding.inflate(layoutInflater)
-
- override fun getCoordinatorLayout() = views.coordinatorLayout
-
- override fun injectWith(injector: ScreenComponent) {
- injector.inject(this)
- }
-
- override fun getMenuRes() = R.menu.menu_room_threads
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- initToolbar()
- initFragment()
- }
-
- private fun initToolbar() {
- configureToolbar(views.roomThreadDetailToolbar)
- getRoomThreadDetailArgs()?.let {
- val matrixItem = MatrixItem.RoomItem(it.roomId, it.displayName, it.avatarUrl)
- avatarRenderer.render(matrixItem, views.roomThreadDetailToolbarImageView)
- }
- }
-
- private fun initFragment() {
- if (isFirstCreation()) {
- getRoomThreadDetailArgs()?.let {
- replaceFragment(
- R.id.roomThreadDetailFragmentContainer,
- RoomDetailFragment::class.java,
- RoomDetailArgs(
- roomId = it.roomId,
- roomThreadDetailArgs = it
- ))
- }
-// replaceFragment(R.id.roomThreadDetailFragmentContainer, RoomThreadDetailFragment::class.java, getRoomThreadDetailArgs(), FRAGMENT_TAG)
- }
- }
-
- private fun getRoomThreadDetailArgs(): RoomThreadDetailArgs? = intent?.extras?.getParcelable(ROOM_THREAD_DETAIL_ARGS)
-
- companion object {
- private val FRAGMENT_TAG = RoomThreadDetailFragment::class.simpleName
- const val ROOM_THREAD_DETAIL_ARGS = "ROOM_THREAD_DETAIL_ARGS"
-
- fun newIntent(context: Context, roomThreadDetailArgs: RoomThreadDetailArgs): Intent {
- return Intent(context, RoomThreadDetailActivity::class.java).apply {
- putExtra(ROOM_THREAD_DETAIL_ARGS, roomThreadDetailArgs)
- }
- }
- }
-}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/detail/RoomThreadDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/detail/ThreadListFragment.kt
similarity index 74%
rename from vector/src/main/java/im/vector/app/features/home/room/threads/detail/RoomThreadDetailFragment.kt
rename to vector/src/main/java/im/vector/app/features/home/room/threads/detail/ThreadListFragment.kt
index fee21128cd..4e870bd53b 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/detail/RoomThreadDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/detail/ThreadListFragment.kt
@@ -16,34 +16,31 @@
package im.vector.app.features.home.room.threads.detail
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.core.view.isVisible
import com.airbnb.mvrx.args
import im.vector.app.core.platform.VectorBaseFragment
-import im.vector.app.databinding.FragmentRoomThreadDetailBinding
-import im.vector.app.features.home.room.threads.detail.arguments.RoomThreadDetailArgs
+import im.vector.app.databinding.FragmentThreadListBinding
+import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
-class RoomThreadDetailFragment @Inject constructor(
+class ThreadListFragment @Inject constructor(
private val session: Session
-) : VectorBaseFragment() {
+) : VectorBaseFragment() {
- private val roomThreadDetailArgs: RoomThreadDetailArgs by args()
+ private val threadTimelineArgs: ThreadTimelineArgs by args()
- override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomThreadDetailBinding {
- return FragmentRoomThreadDetailBinding.inflate(inflater, container, false)
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentThreadListBinding {
+ return FragmentThreadListBinding.inflate(inflater, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
- @SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initTextComposer()
@@ -59,7 +56,7 @@ class RoomThreadDetailFragment @Inject constructor(
}
private fun initTextComposer(){
- views.roomThreadDetailTextComposerView.views.sendButton.isVisible = true
+// views.roomThreadDetailTextComposerView.views.sendButton.isVisible = true
}
}
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 debdf3739c..fdf1a24261 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
@@ -49,10 +49,13 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.debug.DebugMenuActivity
import im.vector.app.features.devtools.RoomDevToolActivity
import im.vector.app.features.home.room.detail.RoomDetailActivity
-import im.vector.app.features.home.room.detail.RoomDetailArgs
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.search.SearchActivity
import im.vector.app.features.home.room.detail.search.SearchArgs
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
+import im.vector.app.features.home.room.threads.ThreadsActivity
+import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
+import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.invite.InviteUsersToRoomActivity
import im.vector.app.features.login.LoginActivity
import im.vector.app.features.login.LoginConfig
@@ -118,7 +121,7 @@ class DefaultNavigator @Inject constructor(
fatalError("Trying to open an unknown room $roomId", vectorPreferences.failFast())
return
}
- val args = RoomDetailArgs(roomId, eventId)
+ val args = TimelineArgs(roomId, eventId)
val intent = RoomDetailActivity.newIntent(context, args)
startActivity(context, intent, buildTask)
}
@@ -141,7 +144,7 @@ class DefaultNavigator @Inject constructor(
startActivity(context, SpaceManageActivity.newIntent(context, spaceId, ManageType.AddRooms), false)
}
is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> {
- val args = RoomDetailArgs(
+ val args = TimelineArgs(
postSwitchSpaceAction.roomId,
eventId = null,
openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet }
@@ -239,7 +242,7 @@ class DefaultNavigator @Inject constructor(
}
override fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData) {
- val args = RoomDetailArgs(roomId, null, sharedData)
+ val args = TimelineArgs(roomId, null, sharedData)
val intent = RoomDetailActivity.newIntent(activity, args)
activity.startActivity(intent)
activity.finish()
@@ -507,4 +510,11 @@ class DefaultNavigator @Inject constructor(
context.startActivity(intent)
}
}
+
+ override fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs) {
+ context.startActivity(ThreadsActivity.newIntent(
+ context = context,
+ threadTimelineArgs = threadTimelineArgs,
+ threadListArgs =null))
+ }
}
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 612643c804..eeeb2a1b35 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
@@ -24,6 +24,7 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.core.util.Pair
import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.displayname.getBestName
+import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.login.LoginConfig
import im.vector.app.features.media.AttachmentData
import im.vector.app.features.pin.PinMode
@@ -140,4 +141,7 @@ interface Navigator {
fun openDevTools(context: Context, roomId: String)
fun openCallTransfer(context: Context, callId: String)
+
+ fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs)
+
}
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
index 92feb3d038..7f41049c21 100755
--- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
@@ -56,7 +56,7 @@ import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.room.detail.RoomDetailActivity
-import im.vector.app.features.home.room.detail.RoomDetailArgs
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
import im.vector.app.features.themes.ThemeUtils
@@ -497,7 +497,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
- .addNextIntent(RoomDetailActivity.newIntent(context, RoomDetailArgs(callInformation.nativeRoomId)))
+ .addNextIntent(RoomDetailActivity.newIntent(context, TimelineArgs(callInformation.nativeRoomId)))
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(contentPendingIntent)
@@ -735,7 +735,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
}
private fun buildOpenRoomIntent(roomId: String): PendingIntent? {
- val roomIntentTap = RoomDetailActivity.newIntent(context, RoomDetailArgs(roomId))
+ val roomIntentTap = RoomDetailActivity.newIntent(context, TimelineArgs(roomId))
roomIntentTap.action = TAP_TO_VIEW_ACTION
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
roomIntentTap.data = Uri.parse("foobar://openRoom?$roomId")
diff --git a/vector/src/main/res/drawable/ic_thread_link_menu_item.xml b/vector/src/main/res/drawable/ic_thread_link_menu_item.xml
new file mode 100644
index 0000000000..779c9d832c
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_thread_link_menu_item.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/vector/src/main/res/drawable/ic_thread_menu_item.xml b/vector/src/main/res/drawable/ic_thread_menu_item.xml
new file mode 100644
index 0000000000..2d77251c53
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_thread_menu_item.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/vector/src/main/res/drawable/ic_thread_share_menu_item.xml b/vector/src/main/res/drawable/ic_thread_share_menu_item.xml
new file mode 100644
index 0000000000..cb863c39bf
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_thread_share_menu_item.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/vector/src/main/res/drawable/ic_thread_view_in_room_menu_item.xml b/vector/src/main/res/drawable/ic_thread_view_in_room_menu_item.xml
new file mode 100644
index 0000000000..f408f99713
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_thread_view_in_room_menu_item.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/activity_room_thread_detail.xml b/vector/src/main/res/layout/activity_room_thread_detail.xml
deleted file mode 100644
index 94c52ab959..0000000000
--- a/vector/src/main/res/layout/activity_room_thread_detail.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vector/src/main/res/layout/activity_room_threads.xml b/vector/src/main/res/layout/activity_room_threads.xml
deleted file mode 100644
index b469c7de42..0000000000
--- a/vector/src/main/res/layout/activity_room_threads.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vector/src/main/res/layout/activity_threads.xml b/vector/src/main/res/layout/activity_threads.xml
new file mode 100644
index 0000000000..c34be9687d
--- /dev/null
+++ b/vector/src/main/res/layout/activity_threads.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/layout/fragment_room_detail.xml b/vector/src/main/res/layout/fragment_room_detail.xml
index 7725cd5e92..5d0e5f3ebd 100644
--- a/vector/src/main/res/layout/fragment_room_detail.xml
+++ b/vector/src/main/res/layout/fragment_room_detail.xml
@@ -26,101 +26,13 @@
android:layout_height="?actionBarSize"
android:transitionName="toolbar">
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/vector/src/main/res/layout/fragment_room_thread_detail.xml b/vector/src/main/res/layout/fragment_room_thread_detail.xml
deleted file mode 100644
index cadc819d28..0000000000
--- a/vector/src/main/res/layout/fragment_room_thread_detail.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/vector/src/main/res/layout/view_room_detail_thread_toolbar.xml b/vector/src/main/res/layout/view_room_detail_thread_toolbar.xml
new file mode 100644
index 0000000000..dcb60a44d7
--- /dev/null
+++ b/vector/src/main/res/layout/view_room_detail_thread_toolbar.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/view_room_detail_toolbar.xml b/vector/src/main/res/layout/view_room_detail_toolbar.xml
new file mode 100644
index 0000000000..fdc3f6819e
--- /dev/null
+++ b/vector/src/main/res/layout/view_room_detail_toolbar.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/menu/menu_thread_timeline.xml b/vector/src/main/res/menu/menu_thread_timeline.xml
new file mode 100644
index 0000000000..4698559bae
--- /dev/null
+++ b/vector/src/main/res/menu/menu_thread_timeline.xml
@@ -0,0 +1,36 @@
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/menu/menu_timeline.xml b/vector/src/main/res/menu/menu_timeline.xml
index 54967f1706..532b63dd38 100644
--- a/vector/src/main/res/menu/menu_timeline.xml
+++ b/vector/src/main/res/menu/menu_timeline.xml
@@ -37,11 +37,20 @@
app:showAsAction="always"
tools:visible="true" />
-
+
+
+ app:showAsAction="always" />
+ -
+
+
+
+
+
+
+
+
+
+
+
\ 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 b781692733..a85bf0629e 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -441,6 +441,7 @@
Are you sure you want to sign out?
Voice Call
Video Call
+ View Threads
Global search
Mark all as read
Historical
@@ -456,6 +457,11 @@
Disable
Return
+
+ View in room
+ Copy link to thread
+ Share
+
Confirmation
Warning
@@ -1023,6 +1029,7 @@
Filter Threads in room
+ Thread
Reason for reporting this content