- Add specific toolbar for threads

- Renamed RoomDetailFragment to TimelineFragment while it should be reused from threads
 - View root thread message in room functionality
This commit is contained in:
ariskotsomitopoulos 2021-11-22 14:02:17 +02:00
parent 3de0f7bf37
commit 586b3d8caa
38 changed files with 766 additions and 625 deletions

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -179,8 +179,7 @@
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" /> <activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" /> <activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" /> <activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
<activity android:name=".features.home.room.threads.RoomThreadsActivity" /> <activity android:name=".features.home.room.threads.ThreadsActivity" />
<activity android:name=".features.home.room.threads.detail.RoomThreadDetailActivity" />
<activity <activity
android:name=".features.home.room.detail.RoomDetailActivity" android:name=".features.home.room.detail.RoomDetailActivity"

View file

@ -55,10 +55,10 @@ import im.vector.app.features.home.HomeDetailFragment
import im.vector.app.features.home.HomeDrawerFragment import im.vector.app.features.home.HomeDrawerFragment
import im.vector.app.features.home.LoadingFragment import im.vector.app.features.home.LoadingFragment
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
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.search.SearchFragment import im.vector.app.features.home.room.detail.search.SearchFragment
import im.vector.app.features.home.room.list.RoomListFragment import im.vector.app.features.home.room.list.RoomListFragment
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailFragment import im.vector.app.features.home.room.threads.detail.ThreadListFragment
import im.vector.app.features.login.LoginCaptchaFragment import im.vector.app.features.login.LoginCaptchaFragment
import im.vector.app.features.login.LoginFragment import im.vector.app.features.login.LoginFragment
import im.vector.app.features.login.LoginGenericTextInputFormFragment import im.vector.app.features.login.LoginGenericTextInputFormFragment
@ -183,8 +183,8 @@ interface FragmentModule {
@Binds @Binds
@IntoMap @IntoMap
@FragmentKey(RoomDetailFragment::class) @FragmentKey(TimelineFragment::class)
fun bindRoomDetailFragment(fragment: RoomDetailFragment): Fragment fun bindRoomDetailFragment(fragment: TimelineFragment): Fragment
@Binds @Binds
@IntoMap @IntoMap
@ -838,6 +838,6 @@ interface FragmentModule {
@Binds @Binds
@IntoMap @IntoMap
@FragmentKey(RoomThreadDetailFragment::class) @FragmentKey(ThreadListFragment::class)
fun bindRoomThreadDetailFragment(fragment: RoomThreadDetailFragment): Fragment fun bindRoomThreadDetailFragment(fragment: ThreadListFragment): Fragment
} }

View file

@ -52,8 +52,7 @@ import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
import im.vector.app.features.home.room.list.RoomListModule import im.vector.app.features.home.room.list.RoomListModule
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.app.features.home.room.threads.RoomThreadsActivity import im.vector.app.features.home.room.threads.ThreadsActivity
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailActivity
import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.invite.InviteUsersToRoomActivity import im.vector.app.features.invite.InviteUsersToRoomActivity
import im.vector.app.features.invite.VectorInviteView import im.vector.app.features.invite.VectorInviteView
@ -176,8 +175,7 @@ interface ScreenComponent {
fun inject(activity: SpaceManageActivity) fun inject(activity: SpaceManageActivity)
fun inject(activity: RoomJoinRuleActivity) fun inject(activity: RoomJoinRuleActivity)
fun inject(activity: SpaceLeaveAdvancedActivity) fun inject(activity: SpaceLeaveAdvancedActivity)
fun inject(activity: RoomThreadsActivity) fun inject(activity: ThreadsActivity)
fun inject(activity: RoomThreadDetailActivity)
/* ========================================================================================== /* ==========================================================================================
* BottomSheets * BottomSheets

View file

@ -58,7 +58,7 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailActivity 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 io.github.hyuwah.draggableviewlib.DraggableView import io.github.hyuwah.draggableviewlib.DraggableView
import io.github.hyuwah.draggableviewlib.setupDraggable import io.github.hyuwah.draggableviewlib.setupDraggable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -573,7 +573,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
private fun returnToChat() { private fun returnToChat() {
val roomId = withState(callViewModel) { it.roomId } val roomId = withState(callViewModel) { it.roomId }
val args = RoomDetailArgs(roomId) val args = TimelineArgs(roomId)
val intent = RoomDetailActivity.newIntent(this, args).apply { val intent = RoomDetailActivity.newIntent(this, args).apply {
flags = FLAG_ACTIVITY_CLEAR_TOP flags = FLAG_ACTIVITY_CLEAR_TOP
} }

View file

@ -21,7 +21,7 @@ import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailActivity 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.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert import im.vector.app.features.popup.VerificationVectorAlert
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
@ -142,7 +142,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
R.drawable.ic_shield_black, R.drawable.ic_shield_black,
shouldBeDisplayedIn = { activity -> shouldBeDisplayedIn = { activity ->
if (activity is RoomDetailActivity) { if (activity is RoomDetailActivity) {
activity.intent?.extras?.getParcelable<RoomDetailArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let { activity.intent?.extras?.getParcelable<TimelineArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
it.roomId != pr.roomId it.roomId != pr.roomId
} ?: true } ?: true
} else true } else true

View file

@ -34,6 +34,7 @@ import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityRoomDetailBinding import im.vector.app.databinding.ActivityRoomDetailBinding
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment 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.matrixto.MatrixToBottomSheet
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import im.vector.app.features.room.RequireActiveMembershipAction import im.vector.app.features.room.RequireActiveMembershipAction
@ -102,16 +103,16 @@ class RoomDetailActivity :
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
waitingView = views.waitingView.waitingView waitingView = views.waitingView.waitingView
val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) { val timelineArgs: TimelineArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!) TimelineArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!)
} else { } else {
intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
} }
if (roomDetailArgs == null) return if (timelineArgs == null) return
currentRoomId = roomDetailArgs.roomId currentRoomId = timelineArgs.roomId
if (isFirstCreation()) { 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) replaceFragment(R.id.roomDetailDrawerContainer, BreadcrumbsFragment::class.java)
} }
@ -147,7 +148,7 @@ class RoomDetailActivity :
if (currentRoomId != switchToRoom.roomId) { if (currentRoomId != switchToRoom.roomId) {
currentRoomId = switchToRoom.roomId currentRoomId = switchToRoom.roomId
requireActiveMembershipViewModel.handle(RequireActiveMembershipAction.ChangeRoom(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 EXTRA_ROOM_ID = "EXTRA_ROOM_ID"
const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT" 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 { return Intent(context, RoomDetailActivity::class.java).apply {
putExtra(EXTRA_ROOM_DETAIL_ARGS, roomDetailArgs) putExtra(EXTRA_ROOM_DETAIL_ARGS, timelineArgs)
} }
} }

View file

@ -153,7 +153,7 @@ class RoomDetailViewModel @AssistedInject constructor(
@JvmStatic @JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? { 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) return fragment.roomDetailViewModelFactory.create(state)
} }
@ -668,20 +668,30 @@ class RoomDetailViewModel @AssistedInject constructor(
private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled() private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled()
fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state -> fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state ->
if (state.asyncRoomSummary()?.membership != Membership.JOIN) { if (state.asyncRoomSummary()?.membership != Membership.JOIN) {
return@withState false return@withState false
} }
when (itemId) {
R.id.timeline_setting -> true if (initialState.isThreadTimeline()) {
R.id.invite -> state.canInvite when (itemId) {
R.id.open_matrix_apps -> true R.id.menu_thread_timeline_more -> true
R.id.voice_call -> state.isWebRTCCallOptionAvailable() else -> false
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. ^ } else {
R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined when (itemId) {
R.id.search -> true R.id.timeline_setting -> true
R.id.dev_tools -> vectorPreferences.developerMode() R.id.invite -> state.canInvite
else -> false 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
}
} }
} }

View file

@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized 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.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.initsync.SyncStatusService
@ -69,12 +70,12 @@ data class RoomDetailViewState(
val rootThreadEventId: String? = null val rootThreadEventId: String? = null
) : MavericksState { ) : MavericksState {
constructor(args: RoomDetailArgs) : this( constructor(args: TimelineArgs) : this(
roomId = args.roomId, roomId = args.roomId,
eventId = args.eventId, eventId = args.eventId,
// Also highlight the target event, if any // Also highlight the target event, if any
highlightedEventId = args.eventId, highlightedEventId = args.eventId,
rootThreadEventId = args.roomThreadDetailArgs?.eventId rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId
) )
fun isWebRTCCallOptionAvailable() = (asyncRoomSummary.invoke()?.joinedMembersCount ?: 0) <= 2 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 hasActiveJitsiWidget() = activeRoomWidgets()?.any { it.type == WidgetType.Jitsi && it.isActive }.orFalse()
fun isDm() = asyncRoomSummary()?.isDirect == true fun isDm() = asyncRoomSummary()?.isDirect == true
fun isThreadTimeline() = rootThreadEventId != null
} }

View file

@ -25,7 +25,6 @@ import android.graphics.Typeface
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable
import android.text.Spannable import android.text.Spannable
import android.text.format.DateUtils import android.text.format.DateUtils
import android.view.HapticFeedbackConstants import android.view.HapticFeedbackConstants
@ -49,7 +48,6 @@ import androidx.core.text.toSpannable
import androidx.core.util.Pair import androidx.core.util.Pair
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.core.view.isGone
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.setFragmentResultListener 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.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.home.AvatarRenderer 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.SendMode
import im.vector.app.features.home.room.detail.composer.TextComposerAction import im.vector.app.features.home.room.detail.composer.TextComposerAction
import im.vector.app.features.home.room.detail.composer.TextComposerView 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.upgrade.MigrateRoomBottomSheet
import im.vector.app.features.home.room.detail.views.RoomDetailLazyLoadedViews 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.detail.widget.RoomWidgetsBottomSheet
import im.vector.app.features.home.room.threads.detail.arguments.RoomThreadDetailArgs import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailActivity
import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.PillImageSpan import im.vector.app.features.html.PillImageSpan
import im.vector.app.features.html.PillsPostProcessor 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.WidgetKind
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import nl.dionsegijn.konfetti.models.Shape import nl.dionsegijn.konfetti.models.Shape
import nl.dionsegijn.konfetti.models.Size import nl.dionsegijn.konfetti.models.Size
import org.billcarsonfr.jsonviewer.JSonViewerDialog import org.billcarsonfr.jsonviewer.JSonViewerDialog
@ -224,16 +221,7 @@ import java.util.UUID
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@Parcelize class TimelineFragment @Inject constructor(
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(
private val session: Session, private val session: Session,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val timelineEventController: TimelineEventController, private val timelineEventController: TimelineEventController,
@ -282,16 +270,16 @@ class RoomDetailFragment @Inject constructor(
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
private val roomDetailArgs: RoomDetailArgs by args() private val timelineArgs: TimelineArgs by args()
private val glideRequests by lazy { private val glideRequests by lazy {
GlideApp.with(this) GlideApp.with(this)
} }
private val pillsPostProcessor by lazy { private val pillsPostProcessor by lazy {
pillsPostProcessorFactory.create(roomDetailArgs.roomId) pillsPostProcessorFactory.create(timelineArgs.roomId)
} }
private val autoCompleter: AutoCompleter by lazy { private val autoCompleter: AutoCompleter by lazy {
autoCompleterFactory.create(roomDetailArgs.roomId, isThreadTimeLine()) autoCompleterFactory.create(timelineArgs.roomId, isThreadTimeLine())
} }
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel() private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
@ -308,6 +296,8 @@ class RoomDetailFragment @Inject constructor(
override fun getMenuRes() = R.menu.menu_timeline override fun getMenuRes() = R.menu.menu_timeline
private lateinit var sharedActionViewModel: MessageSharedActionViewModel private lateinit var sharedActionViewModel: MessageSharedActionViewModel
private lateinit var sharedActivityActionViewModel: RoomDetailSharedActionViewModel
private lateinit var knownCallsViewModel: SharedKnownCallsViewModel private lateinit var knownCallsViewModel: SharedKnownCallsViewModel
private lateinit var layoutManager: LinearLayoutManager private lateinit var layoutManager: LinearLayoutManager
@ -341,10 +331,11 @@ class RoomDetailFragment @Inject constructor(
lifecycle.addObserver(ConferenceEventObserver(vectorBaseActivity, this::onBroadcastJitsiEvent)) lifecycle.addObserver(ConferenceEventObserver(vectorBaseActivity, this::onBroadcastJitsiEvent))
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
sharedActivityActionViewModel = activityViewModelProvider.get(RoomDetailSharedActionViewModel::class.java)
knownCallsViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java) knownCallsViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java)
attachmentsHelper = AttachmentsHelper(requireContext(), this).register() attachmentsHelper = AttachmentsHelper(requireContext(), this).register()
callActionsHandler = StartCallActionsHandler( callActionsHandler = StartCallActionsHandler(
roomId = roomDetailArgs.roomId, roomId = timelineArgs.roomId,
fragment = this, fragment = this,
vectorPreferences = vectorPreferences, vectorPreferences = vectorPreferences,
roomDetailViewModel = roomDetailViewModel, roomDetailViewModel = roomDetailViewModel,
@ -355,11 +346,7 @@ class RoomDetailFragment @Inject constructor(
) )
keyboardStateUtils = KeyboardStateUtils(requireActivity()) keyboardStateUtils = KeyboardStateUtils(requireActivity())
lazyLoadedViews.bind(views) lazyLoadedViews.bind(views)
if (isThreadTimeLine()) { setupToolbar(views.roomToolbar)
views.roomToolbar.isGone = true
} else {
setupToolbar(views.roomToolbar)
}
setupRecyclerView() setupRecyclerView()
setupComposer() setupComposer()
setupNotificationView() setupNotificationView()
@ -370,8 +357,8 @@ class RoomDetailFragment @Inject constructor(
setupRemoveJitsiWidgetView() setupRemoveJitsiWidgetView()
setupVoiceMessageView() setupVoiceMessageView()
views.roomToolbarContentView.debouncedClicks { views.includeRoomToolbar.roomToolbarContentView.debouncedClicks {
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId) navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
} }
sharedActionViewModel sharedActionViewModel
@ -456,7 +443,7 @@ class RoomDetailFragment @Inject constructor(
RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView() RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView()
is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it) is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(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.OpenSetRoomAvatarDialog -> galleryOrCameraDialogHelper.show()
RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings() RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings()
is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item -> is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item ->
@ -532,7 +519,7 @@ class RoomDetailFragment @Inject constructor(
private fun handleShowRoomUpgradeDialog(roomDetailViewEvents: TextComposerViewEvents.ShowRoomUpgradeDialog) { private fun handleShowRoomUpgradeDialog(roomDetailViewEvents: TextComposerViewEvents.ShowRoomUpgradeDialog) {
val tag = MigrateRoomBottomSheet::javaClass.name val tag = MigrateRoomBottomSheet::javaClass.name
MigrateRoomBottomSheet.newInstance(roomDetailArgs.roomId, roomDetailViewEvents.newVersion) MigrateRoomBottomSheet.newInstance(timelineArgs.roomId, roomDetailViewEvents.newVersion)
.show(parentFragmentManager, tag) .show(parentFragmentManager, tag)
} }
@ -578,7 +565,7 @@ class RoomDetailFragment @Inject constructor(
private fun handleOpenRoomSettings() { private fun handleOpenRoomSettings() {
navigator.openRoomProfile( navigator.openRoomProfile(
requireContext(), requireContext(),
roomDetailArgs.roomId, timelineArgs.roomId,
RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS
) )
} }
@ -600,7 +587,7 @@ class RoomDetailFragment @Inject constructor(
WidgetArgs( WidgetArgs(
baseUrl = it.domain, baseUrl = it.domain,
kind = WidgetKind.ROOM, kind = WidgetKind.ROOM,
roomId = roomDetailArgs.roomId, roomId = timelineArgs.roomId,
widgetId = it.widget.widgetId widgetId = it.widget.widgetId
) )
).apply { ).apply {
@ -626,7 +613,7 @@ class RoomDetailFragment @Inject constructor(
navigator.openIntegrationManager( navigator.openIntegrationManager(
context = requireContext(), context = requireContext(),
activityResultLauncher = integrationManagerActivityResultLauncher, activityResultLauncher = integrationManagerActivityResultLauncher,
roomId = roomDetailArgs.roomId, roomId = timelineArgs.roomId,
integId = null, integId = null,
screen = screen screen = screen
) )
@ -717,11 +704,11 @@ class RoomDetailFragment @Inject constructor(
} }
private fun joinJitsiRoom(jitsiWidget: Widget, enableVideo: Boolean) { 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) { 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) { private fun startOpenFileIntent(action: RoomDetailViewEvents.OpenFile) {
@ -795,7 +782,7 @@ class RoomDetailFragment @Inject constructor(
} }
private fun handleShareData() { private fun handleShareData() {
when (val sharedData = roomDetailArgs.sharedData) { when (val sharedData = timelineArgs.sharedData) {
is SharedData.Text -> { is SharedData.Text -> {
textComposerViewModel.handle(TextComposerAction.EnterRegularMode(sharedData.text, fromSharing = true)) textComposerViewModel.handle(TextComposerAction.EnterRegularMode(sharedData.text, fromSharing = true))
} }
@ -808,7 +795,7 @@ class RoomDetailFragment @Inject constructor(
} }
private fun handleSpaceShare() { private fun handleSpaceShare() {
roomDetailArgs.openShareSpaceForId?.let { spaceId -> timelineArgs.openShareSpaceForId?.let { spaceId ->
ShareSpaceBottomSheet.show(childFragmentManager, spaceId, true) ShareSpaceBottomSheet.show(childFragmentManager, spaceId, true)
view?.post { view?.post {
handleChatEffect(ChatEffect.CONFETTI) handleChatEffect(ChatEffect.CONFETTI)
@ -909,7 +896,6 @@ class RoomDetailFragment @Inject constructor(
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
if (isThreadTimeLine()) return
// We use a custom layout for this menu item, so we need to set a ClickListener // 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 -> menu.findItem(R.id.open_matrix_apps)?.let { menuItem ->
menuItem.actionView.setOnClickListener { menuItem.actionView.setOnClickListener {
@ -923,15 +909,11 @@ class RoomDetailFragment @Inject constructor(
} }
override fun onPrepareOptionsMenu(menu: Menu) { override fun onPrepareOptionsMenu(menu: Menu) {
if (isThreadTimeLine()) {
menu.forEach {
it.isVisible = false
}
return
}
menu.forEach { menu.forEach {
it.isVisible = roomDetailViewModel.isMenuItemVisible(it.itemId) it.isVisible = roomDetailViewModel.isMenuItemVisible(it.itemId)
} }
withState(roomDetailViewModel) { state -> withState(roomDetailViewModel) { state ->
// Set the visual state of the call buttons (voice/video) to enabled/disabled according to user permissions // 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 val hasCallInRoom = callManager.getCallsByRoomId(state.roomId).isNotEmpty() || state.jitsiState.hasJoined
@ -968,41 +950,72 @@ class RoomDetailFragment @Inject constructor(
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.invite -> { R.id.invite -> {
navigator.openInviteUsersToRoom(requireActivity(), roomDetailArgs.roomId) navigator.openInviteUsersToRoom(requireActivity(), timelineArgs.roomId)
true true
} }
R.id.timeline_setting -> { R.id.timeline_setting -> {
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId) navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
true true
} }
R.id.open_matrix_apps -> { R.id.open_matrix_apps -> {
roomDetailViewModel.handle(RoomDetailAction.ManageIntegrations) roomDetailViewModel.handle(RoomDetailAction.ManageIntegrations)
true true
} }
R.id.voice_call -> { R.id.voice_call -> {
callActionsHandler.onVoiceCallClicked() callActionsHandler.onVoiceCallClicked()
true true
} }
R.id.video_call -> { R.id.video_call -> {
callActionsHandler.onVideoCallClicked() callActionsHandler.onVideoCallClicked()
true true
} }
R.id.search -> { R.id.threads -> {
requireActivity().toast("View All Threads")
true
}
R.id.search -> {
handleSearchAction() handleSearchAction()
true true
} }
R.id.dev_tools -> { R.id.dev_tools -> {
navigator.openDevTools(requireContext(), roomDetailArgs.roomId) navigator.openDevTools(requireContext(), timelineArgs.roomId)
true 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() { private fun handleSearchAction() {
if (session.getRoom(roomDetailArgs.roomId)?.isEncrypted() == false) { if (session.getRoom(timelineArgs.roomId)?.isEncrypted() == false) {
navigator.openSearch(requireContext(), roomDetailArgs.roomId) navigator.openSearch(requireContext(), timelineArgs.roomId)
} else { } else {
showDialogWithMessage(getString(R.string.search_is_not_supported_in_e2e_room)) showDialogWithMessage(getString(R.string.search_is_not_supported_in_e2e_room))
} }
@ -1080,7 +1093,7 @@ class RoomDetailFragment @Inject constructor(
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
notificationDrawerManager.setCurrentRoom(roomDetailArgs.roomId) notificationDrawerManager.setCurrentRoom(timelineArgs.roomId)
roomDetailPendingActionStore.data?.let { handlePendingAction(it) } roomDetailPendingActionStore.data?.let { handlePendingAction(it) }
roomDetailPendingActionStore.data = null roomDetailPendingActionStore.data = null
@ -1322,7 +1335,7 @@ class RoomDetailFragment @Inject constructor(
views.composerLayout.callback = object : TextComposerView.Callback { views.composerLayout.callback = object : TextComposerView.Callback {
override fun onAddAttachment() { override fun onAddAttachment() {
if (!::attachmentTypeSelector.isInitialized) { 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) attachmentTypeSelector.show(views.composerLayout.views.attachmentButton, keyboardStateUtils.isKeyboardShowing)
} }
@ -1420,7 +1433,7 @@ class RoomDetailFragment @Inject constructor(
} else if (summary?.membership == Membership.INVITE && inviter != null) { } else if (summary?.membership == Membership.INVITE && inviter != null) {
views.hideComposerViews() views.hideComposerViews()
lazyLoadedViews.inviteView(true)?.apply { lazyLoadedViews.inviteView(true)?.apply {
callback = this@RoomDetailFragment callback = this@TimelineFragment
isVisible = true isVisible = true
render(inviter, VectorInviteView.Mode.LARGE, mainState.changeMembershipState) render(inviter, VectorInviteView.Mode.LARGE, mainState.changeMembershipState)
setOnClickListener { } setOnClickListener { }
@ -1437,23 +1450,35 @@ class RoomDetailFragment @Inject constructor(
} }
private fun renderToolbar(roomSummary: RoomSummary?, typingMessage: String?) { private fun renderToolbar(roomSummary: RoomSummary?, typingMessage: String?) {
if (roomSummary == null) { if (!isThreadTimeLine()) {
views.roomToolbarContentView.isClickable = false 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 { } else {
views.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN views.includeRoomToolbar.roomToolbarContentView.isVisible = false
views.roomToolbarTitleView.text = roomSummary.displayName views.includeThreadToolbar.roomToolbarThreadConstraintLayout.isVisible = true
avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView) timelineArgs.threadTimelineArgs?.let {
renderSubTitle(typingMessage, roomSummary.topic) val matrixItem = MatrixItem.RoomItem(it.roomId, it.displayName, it.avatarUrl)
views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel) avatarRenderer.render(matrixItem, views.includeThreadToolbar.roomToolbarThreadImageView)
views.roomToolbarPresenceImageView.render(roomSummary.isDirect, roomSummary.directUserPresence) views.includeThreadToolbar.roomToolbarThreadSubtitleTextView.text = it.displayName
views.roomToolbarPublicImageView.isVisible = roomSummary.isPublic && !roomSummary.isDirect }
} }
} }
private fun renderSubTitle(typingMessage: String?, topic: String) { private fun renderSubTitle(typingMessage: String?, topic: String) {
// TODO Temporary place to put typing data // TODO Temporary place to put typing data
val subtitle = typingMessage?.takeIf { it.isNotBlank() } ?: topic val subtitle = typingMessage?.takeIf { it.isNotBlank() } ?: topic
views.roomToolbarSubtitleView.apply { views.includeRoomToolbar.roomToolbarSubtitleView.apply {
setTextOrHide(subtitle) setTextOrHide(subtitle)
if (typingMessage.isNullOrBlank()) { if (typingMessage.isNullOrBlank()) {
setTextColor(colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) setTextColor(colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
@ -1592,14 +1617,14 @@ class RoomDetailFragment @Inject constructor(
is RoomDetailAction.RequestVerification -> { is RoomDetailAction.RequestVerification -> {
Timber.v("## SAS RequestVerification action") Timber.v("## SAS RequestVerification action")
VerificationBottomSheet.withArgs( VerificationBottomSheet.withArgs(
roomDetailArgs.roomId, timelineArgs.roomId,
data.userId data.userId
).show(parentFragmentManager, "REQ") ).show(parentFragmentManager, "REQ")
} }
is RoomDetailAction.AcceptVerificationRequest -> { is RoomDetailAction.AcceptVerificationRequest -> {
Timber.v("## SAS AcceptVerificationRequest action") Timber.v("## SAS AcceptVerificationRequest action")
VerificationBottomSheet.withArgs( VerificationBottomSheet.withArgs(
roomDetailArgs.roomId, timelineArgs.roomId,
data.otherUserId, data.otherUserId,
data.transactionId data.transactionId
).show(parentFragmentManager, "REQ") ).show(parentFragmentManager, "REQ")
@ -1609,7 +1634,7 @@ class RoomDetailFragment @Inject constructor(
VerificationBottomSheet().apply { VerificationBottomSheet().apply {
arguments = Bundle().apply { arguments = Bundle().apply {
putParcelable(Mavericks.KEY_ARG, VerificationBottomSheet.VerificationArgs( putParcelable(Mavericks.KEY_ARG, VerificationBottomSheet.VerificationArgs(
otherUserId, data.transactionId, roomId = roomDetailArgs.roomId)) otherUserId, data.transactionId, roomId = timelineArgs.roomId))
} }
}.show(parentFragmentManager, "REQ") }.show(parentFragmentManager, "REQ")
} }
@ -1624,7 +1649,7 @@ class RoomDetailFragment @Inject constructor(
.launch(requireActivity(), url, object : NavigationInterceptor { .launch(requireActivity(), url, object : NavigationInterceptor {
override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean {
// Same room? // Same room?
if (roomId == roomDetailArgs.roomId) { if (roomId == timelineArgs.roomId) {
// Navigation to same room // Navigation to same room
if (eventId == null) { if (eventId == null) {
showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room)) 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) { override fun onImageMessageClicked(messageImageContent: MessageImageInfoContent, mediaData: ImageContentRenderer.Data, view: View) {
navigator.openMediaViewer( navigator.openMediaViewer(
activity = requireActivity(), activity = requireActivity(),
roomId = roomDetailArgs.roomId, roomId = timelineArgs.roomId,
mediaData = mediaData, mediaData = mediaData,
view = view view = view
) { pairs -> ) { pairs ->
@ -1703,7 +1728,7 @@ class RoomDetailFragment @Inject constructor(
override fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View) { override fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View) {
navigator.openMediaViewer( navigator.openMediaViewer(
activity = requireActivity(), activity = requireActivity(),
roomId = roomDetailArgs.roomId, roomId = timelineArgs.roomId,
mediaData = mediaData, mediaData = mediaData,
view = view view = view
) { pairs -> ) { pairs ->
@ -1738,7 +1763,7 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.LoadMoreTimelineEvents(direction)) 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) { when (messageContent) {
is MessageVerificationRequestContent -> { is MessageVerificationRequestContent -> {
roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, null)) roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, null))
@ -1751,11 +1776,14 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.TapOnFailedToDecrypt(informationData.eventId)) roomDetailViewModel.handle(RoomDetailAction.TapOnFailedToDecrypt(informationData.eventId))
} }
} }
if (isRootThreadEvent) {
navigateToThreadTimeline(informationData.eventId)
}
} }
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean { override fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean {
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
val roomId = roomDetailArgs.roomId val roomId = timelineArgs.roomId
this.view?.hideKeyboard() this.view?.hideKeyboard()
MessageActionsBottomSheet MessageActionsBottomSheet
@ -1786,7 +1814,7 @@ class RoomDetailFragment @Inject constructor(
} }
private fun openRoomMemberProfile(userId: String) { 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) { override fun onMemberNameClicked(informationData: MessageInformationData) {
@ -1804,12 +1832,12 @@ class RoomDetailFragment @Inject constructor(
} }
override fun onLongClickOnReactionPill(informationData: MessageInformationData, reaction: String) { override fun onLongClickOnReactionPill(informationData: MessageInformationData, reaction: String) {
ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, informationData) ViewReactionsBottomSheet.newInstance(timelineArgs.roomId, informationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS") .show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
} }
override fun onEditedDecorationClicked(informationData: MessageInformationData) { override fun onEditedDecorationClicked(informationData: MessageInformationData) {
ViewEditHistoryBottomSheet.newInstance(roomDetailArgs.roomId, informationData) ViewEditHistoryBottomSheet.newInstance(timelineArgs.roomId, informationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_EDITS") .show(requireActivity().supportFragmentManager, "DISPLAY_EDITS")
} }
@ -1921,7 +1949,7 @@ class RoomDetailFragment @Inject constructor(
emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId)) emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId))
} }
is EventSharedAction.ViewReactions -> { is EventSharedAction.ViewReactions -> {
ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData) ViewReactionsBottomSheet.newInstance(timelineArgs.roomId, action.messageInformationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS") .show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
} }
is EventSharedAction.Copy -> { is EventSharedAction.Copy -> {
@ -1978,20 +2006,13 @@ class RoomDetailFragment @Inject constructor(
} }
is EventSharedAction.ReplyInThread -> { is EventSharedAction.ReplyInThread -> {
if (!views.voiceMessageRecorderView.isActive()) { if (!views.voiceMessageRecorderView.isActive()) {
context?.let { navigateToThreadTimeline(action.eventId)
val roomThreadDetailArgs = RoomThreadDetailArgs(
roomId = roomDetailArgs.roomId,
displayName = roomDetailViewModel.getRoomSummary()?.displayName,
avatarUrl = roomDetailViewModel.getRoomSummary()?.avatarUrl,
eventId = action.eventId)
startActivity(RoomThreadDetailActivity.newIntent(it, roomThreadDetailArgs))
}
} else { } else {
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit) requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
} }
} }
is EventSharedAction.CopyPermalink -> { 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) copyToClipboard(requireContext(), permalink, false)
showSnackWithMessage(getString(R.string.copied_to_clipboard)) showSnackWithMessage(getString(R.string.copied_to_clipboard))
} }
@ -2114,15 +2135,31 @@ class RoomDetailFragment @Inject constructor(
.show() .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 // VectorInviteView.Callback
override fun onAcceptInvite() { override fun onAcceptInvite() {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId) notificationDrawerManager.clearMemberShipNotificationForRoom(timelineArgs.roomId)
roomDetailViewModel.handle(RoomDetailAction.AcceptInvite) roomDetailViewModel.handle(RoomDetailAction.AcceptInvite)
} }
override fun onRejectInvite() { override fun onRejectInvite() {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId) notificationDrawerManager.clearMemberShipNotificationForRoom(timelineArgs.roomId)
roomDetailViewModel.handle(RoomDetailAction.RejectInvite) 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
} }

View file

@ -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

View file

@ -29,7 +29,7 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.command.CommandParser import im.vector.app.features.command.CommandParser
import im.vector.app.features.command.ParsedCommand import im.vector.app.features.command.ParsedCommand
import im.vector.app.features.home.room.detail.ChatEffect 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.composer.rainbow.RainbowGenerator
import im.vector.app.features.home.room.detail.toMessageType import im.vector.app.features.home.room.detail.toMessageType
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory 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.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session 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.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.toContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
@ -772,7 +771,7 @@ class TextComposerViewModel @AssistedInject constructor(
@JvmStatic @JvmStatic
override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel { 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) return fragment.textComposerViewModelFactory.create(state)
} }
} }

View file

@ -17,7 +17,7 @@
package im.vector.app.features.home.room.detail.composer package im.vector.app.features.home.room.detail.composer
import com.airbnb.mvrx.MavericksState 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 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
/** /**
@ -53,9 +53,9 @@ data class TextComposerViewState(
val isComposerVisible: Boolean val isComposerVisible: Boolean
get() = canSendMessage && !isVoiceRecording get() = canSendMessage && !isVoiceRecording
constructor(args: RoomDetailArgs) : this( constructor(args: TimelineArgs) : this(
roomId = args.roomId, roomId = args.roomId,
rootThreadEventId = args.roomThreadDetailArgs?.eventId) rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId)
fun isInThreadTimeline(): Boolean = rootThreadEventId != null fun isInThreadTimeline(): Boolean = rootThreadEventId != null
} }

View file

@ -140,7 +140,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
} }
interface BaseCallback { 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 fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean
} }

View file

@ -43,7 +43,7 @@ class MessageItemAttributesFactory @Inject constructor(
callback?.onEventLongClicked(informationData, messageContent, view) ?: false callback?.onEventLongClicked(informationData, messageContent, view) ?: false
}, },
itemClickListener = { view -> itemClickListener = { view ->
callback?.onEventCellClicked(informationData, messageContent, view) callback?.onEventCellClicked(informationData, messageContent, view, threadDetails?.isRootThread ?: false)
}, },
memberClickListener = { memberClickListener = {
callback?.onMemberNameClicked(informationData) callback?.onMemberNameClicked(informationData)

View file

@ -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<ActivityRoomThreadsBinding>() {
// 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)
}
}
}

View file

@ -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<ActivityThreadsBinding>(), 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()
}
}

View file

@ -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

View file

@ -14,15 +14,15 @@
* limitations under the License. * 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 android.os.Parcelable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class RoomThreadDetailArgs( data class ThreadTimelineArgs(
val roomId: String, val roomId: String,
val displayName: String?, val displayName: String?,
val avatarUrl: String?, val avatarUrl: String?,
val eventId: String? = null, val rootThreadEventId: String? = null
) : Parcelable ) : Parcelable

View file

@ -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<ActivityRoomThreadDetailBinding>() {
@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)
}
}
}
}

View file

@ -16,34 +16,31 @@
package im.vector.app.features.home.room.threads.detail package im.vector.app.features.home.room.threads.detail
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible
import com.airbnb.mvrx.args import com.airbnb.mvrx.args
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentRoomThreadDetailBinding import im.vector.app.databinding.FragmentThreadListBinding
import im.vector.app.features.home.room.threads.detail.arguments.RoomThreadDetailArgs import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject import javax.inject.Inject
class RoomThreadDetailFragment @Inject constructor( class ThreadListFragment @Inject constructor(
private val session: Session private val session: Session
) : VectorBaseFragment<FragmentRoomThreadDetailBinding>() { ) : VectorBaseFragment<FragmentThreadListBinding>() {
private val roomThreadDetailArgs: RoomThreadDetailArgs by args() private val threadTimelineArgs: ThreadTimelineArgs by args()
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomThreadDetailBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentThreadListBinding {
return FragmentRoomThreadDetailBinding.inflate(inflater, container, false) return FragmentThreadListBinding.inflate(inflater, container, false)
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initTextComposer() initTextComposer()
@ -59,7 +56,7 @@ class RoomThreadDetailFragment @Inject constructor(
} }
private fun initTextComposer(){ private fun initTextComposer(){
views.roomThreadDetailTextComposerView.views.sendButton.isVisible = true // views.roomThreadDetailTextComposerView.views.sendButton.isVisible = true
} }
} }

View file

@ -49,10 +49,13 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.debug.DebugMenuActivity import im.vector.app.features.debug.DebugMenuActivity
import im.vector.app.features.devtools.RoomDevToolActivity import im.vector.app.features.devtools.RoomDevToolActivity
import im.vector.app.features.home.room.detail.RoomDetailActivity 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.SearchActivity
import im.vector.app.features.home.room.detail.search.SearchArgs 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.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.invite.InviteUsersToRoomActivity
import im.vector.app.features.login.LoginActivity import im.vector.app.features.login.LoginActivity
import im.vector.app.features.login.LoginConfig 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()) fatalError("Trying to open an unknown room $roomId", vectorPreferences.failFast())
return return
} }
val args = RoomDetailArgs(roomId, eventId) val args = TimelineArgs(roomId, eventId)
val intent = RoomDetailActivity.newIntent(context, args) val intent = RoomDetailActivity.newIntent(context, args)
startActivity(context, intent, buildTask) startActivity(context, intent, buildTask)
} }
@ -141,7 +144,7 @@ class DefaultNavigator @Inject constructor(
startActivity(context, SpaceManageActivity.newIntent(context, spaceId, ManageType.AddRooms), false) startActivity(context, SpaceManageActivity.newIntent(context, spaceId, ManageType.AddRooms), false)
} }
is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> { is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> {
val args = RoomDetailArgs( val args = TimelineArgs(
postSwitchSpaceAction.roomId, postSwitchSpaceAction.roomId,
eventId = null, eventId = null,
openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet } openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet }
@ -239,7 +242,7 @@ class DefaultNavigator @Inject constructor(
} }
override fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData) { 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) val intent = RoomDetailActivity.newIntent(activity, args)
activity.startActivity(intent) activity.startActivity(intent)
activity.finish() activity.finish()
@ -507,4 +510,11 @@ class DefaultNavigator @Inject constructor(
context.startActivity(intent) context.startActivity(intent)
} }
} }
override fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs) {
context.startActivity(ThreadsActivity.newIntent(
context = context,
threadTimelineArgs = threadTimelineArgs,
threadListArgs =null))
}
} }

View file

@ -24,6 +24,7 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.core.util.Pair import androidx.core.util.Pair
import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.displayname.getBestName 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.login.LoginConfig
import im.vector.app.features.media.AttachmentData import im.vector.app.features.media.AttachmentData
import im.vector.app.features.pin.PinMode import im.vector.app.features.pin.PinMode
@ -140,4 +141,7 @@ interface Navigator {
fun openDevTools(context: Context, roomId: String) fun openDevTools(context: Context, roomId: String)
fun openCallTransfer(context: Context, callId: String) fun openCallTransfer(context: Context, callId: String)
fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs)
} }

View file

@ -56,7 +56,7 @@ import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.room.detail.RoomDetailActivity 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.VectorPreferences
import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
@ -497,7 +497,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
val contentPendingIntent = TaskStackBuilder.create(context) val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(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) .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(contentPendingIntent) builder.setContentIntent(contentPendingIntent)
@ -735,7 +735,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
} }
private fun buildOpenRoomIntent(roomId: String): PendingIntent? { 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 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 // 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") roomIntentTap.data = Uri.parse("foobar://openRoom?$roomId")

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12.604,5.761L13.1741,5.1909C14.7488,3.6163 17.2867,3.6012 18.8428,5.1572C20.3988,6.7132 20.3837,9.251 18.809,10.8257L16.1004,13.5342C14.5257,15.1089 11.9877,15.124 10.4317,13.568M11.3962,18.2387L10.8259,18.8091C9.2512,20.3837 6.7133,20.3988 5.1572,18.8428C3.6012,17.2868 3.6163,14.749 5.191,13.1743L7.8995,10.4659C9.4741,8.8913 12.0121,8.8762 13.5681,10.4322"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M2,5C2,3.3431 3.3431,2 5,2H19C20.6565,2 22,3.3422 22,4.9994V17C22,18.6569 20.6569,20 19,20H7.6667C7.4503,20 7.2398,20.0702 7.0667,20.2L3.6,22.8C3.297,23.0273 2.8916,23.0638 2.5528,22.8944C2.214,22.725 2,22.3788 2,22V5ZM5,9C5,8.4477 5.4477,8 6,8H18C18.5523,8 19,8.4477 19,9C19,9.5523 18.5523,10 18,10H6C5.4477,10 5,9.5523 5,9ZM6,12C5.4477,12 5,12.4477 5,13C5,13.5523 5.4477,14 6,14H12C12.5523,14 13,13.5523 13,13C13,12.4477 12.5523,12 12,12H6Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16,7C16,6.4477 16.4477,6 17,6C17.5523,6 18,6.4477 18,7C18,7.5523 17.5523,8 17,8C16.4477,8 16,7.5523 16,7ZM17,4C15.3431,4 14,5.3432 14,7C14,7.0876 14.0038,7.1744 14.0111,7.2601L8.1611,9.9192C7.6154,9.3525 6.8489,9 6,9C4.3432,9 3,10.3431 3,12C3,13.6569 4.3432,15 6,15C6.8488,15 7.6154,14.6474 8.1611,14.0808L14.0111,16.7399C14.0038,16.8256 14,16.9124 14,17C14,18.6569 15.3431,20 17,20C18.6569,20 20,18.6569 20,17C20,15.3431 18.6569,14 17,14C16.1511,14 15.3846,14.3526 14.8389,14.9192L8.9889,12.2601C8.9962,12.1744 9,12.0876 9,12C9,11.9124 8.9962,11.8256 8.9889,11.7399L14.8389,9.0808C15.3846,9.6475 16.1511,10 17,10C18.6569,10 20,8.6568 20,7C20,5.3432 18.6569,4 17,4ZM17,16C16.4477,16 16,16.4477 16,17C16,17.5523 16.4477,18 17,18C17.5523,18 18,17.5523 18,17C18,16.4477 17.5523,16 17,16ZM5,12C5,11.4477 5.4477,11 6,11C6.5523,11 7,11.4477 7,12C7,12.5523 6.5523,13 6,13C5.4477,13 5,12.5523 5,12Z"
android:fillColor="#737D8C"
android:fillType="evenOdd"/>
<path
android:pathData="M17,17m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
android:fillColor="#737D8C"/>
<path
android:pathData="M17,7m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
android:fillColor="#737D8C"/>
<path
android:pathData="M6,12m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
android:fillColor="#737D8C"/>
</vector>

View file

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,4H21"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
<path
android:pathData="M3,10H16"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
<path
android:pathData="M3,14H11"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
<path
android:pathData="M3,20H21"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,88 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/roomThreadDetailToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStart="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/roomThreadDetailToolbarConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/roomThreadDetailToolbarTitleTextView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/a11y_beta"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/roomThreadDetailToolbarImageView"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_presence_offline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomThreadDetailToolbarTitleTextView"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<TextView
android:id="@+id/roomThreadDetailToolbarSubtitleTextView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomThreadDetailToolbarImageView"
app:layout_constraintTop_toTopOf="parent"
tools:text="RoomName"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/roomThreadDetailFragmentContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,88 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/roomThreadsToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStart="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/roomThreadsToolbarConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/roomThreadsToolbarTitleTextView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/a11y_beta"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/roomThreadsToolbarImageView"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_presence_offline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomThreadsToolbarTitleTextView"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<TextView
android:id="@+id/roomThreadsToolbarSubtitleTextView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomThreadsToolbarImageView"
app:layout_constraintTop_toTopOf="parent"
tools:text="RoomName"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/roomThreadsFragmentContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/threadsActivityFragmentContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -26,101 +26,13 @@
android:layout_height="?actionBarSize" android:layout_height="?actionBarSize"
android:transitionName="toolbar"> android:transitionName="toolbar">
<androidx.constraintlayout.widget.ConstraintLayout <include
android:id="@+id/roomToolbarContentView" android:id="@+id/includeThreadToolbar"
android:layout_width="match_parent" layout="@layout/view_room_detail_thread_toolbar" />
android:layout_height="match_parent">
<ImageView <include
android:id="@+id/roomToolbarAvatarImageView" android:id="@+id/includeRoomToolbar"
android:layout_width="40dp" layout="@layout/view_room_detail_toolbar" />
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/roomToolbarDecorationImageView"
android:layout_width="17dp"
android:layout_height="17dp"
android:layout_marginStart="5dp"
android:layout_marginTop="2dp"
app:layout_constraintBottom_toBottomOf="@+id/roomToolbarTitleView"
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
app:layout_constraintTop_toTopOf="@+id/roomToolbarTitleView" />
<im.vector.app.core.ui.views.PresenceStateImageView
android:id="@+id/roomToolbarPresenceImageView"
android:layout_width="12dp"
android:layout_height="12dp"
android:background="@drawable/background_circle"
android:padding="2dp"
android:visibility="gone"
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="20dp"
tools:ignore="MissingConstraints"
tools:layout_constraintCircleRadius="8dp"
tools:src="@drawable/ic_presence_offline"
tools:visibility="visible" />
<ImageView
android:id="@+id/roomToolbarPublicImageView"
android:layout_width="13dp"
android:layout_height="13dp"
android:background="@drawable/background_circle"
android:contentDescription="@string/a11y_public_room"
android:padding="1dp"
android:src="@drawable/ic_public_room"
android:visibility="gone"
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="20dp"
tools:ignore="MissingConstraints"
tools:visibility="visible" />
<TextView
android:id="@+id/roomToolbarTitleView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toTopOf="@+id/roomToolbarSubtitleView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/roomToolbarDecorationImageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:layout_goneMarginStart="7dp"
tools:text="@sample/rooms.json/data/name" />
<TextView
android:id="@+id/roomToolbarSubtitleView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
app:layout_constraintTop_toBottomOf="@+id/roomToolbarTitleView"
tools:text="@sample/rooms.json/data/topic"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.MaterialToolbar> </com.google.android.material.appbar.MaterialToolbar>

View file

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/roomThreadDetailRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:overScrollMode="always"
app:layout_constraintBottom_toTopOf="@id/roomThreadDetailTextComposerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<im.vector.app.features.home.room.detail.composer.TextComposerView
android:id="@+id/roomThreadDetailTextComposerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:minHeight="56dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/roomToolbarThreadConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:visibility="gone"
android:visibility="gone">
<TextView
android:id="@+id/roomToolbarThreadTitleTextView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/room_threads_title"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/roomToolbarThreadImageView"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_presence_offline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomToolbarThreadTitleTextView"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<TextView
android:id="@+id/roomToolbarThreadSubtitleTextView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomToolbarThreadImageView"
app:layout_constraintTop_toTopOf="parent"
tools:text="RoomName"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/roomToolbarContentView"
tools:visibility="visible"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/roomToolbarAvatarImageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/roomToolbarDecorationImageView"
android:layout_width="17dp"
android:layout_height="17dp"
android:layout_marginStart="5dp"
android:layout_marginTop="2dp"
app:layout_constraintBottom_toBottomOf="@+id/roomToolbarTitleView"
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
app:layout_constraintTop_toTopOf="@+id/roomToolbarTitleView" />
<im.vector.app.core.ui.views.PresenceStateImageView
android:id="@+id/roomToolbarPresenceImageView"
android:layout_width="12dp"
android:layout_height="12dp"
android:background="@drawable/background_circle"
android:padding="2dp"
android:visibility="gone"
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="20dp"
tools:ignore="MissingConstraints"
tools:layout_constraintCircleRadius="8dp"
tools:src="@drawable/ic_presence_offline"
tools:visibility="visible" />
<ImageView
android:id="@+id/roomToolbarPublicImageView"
android:layout_width="13dp"
android:layout_height="13dp"
android:background="@drawable/background_circle"
android:contentDescription="@string/a11y_public_room"
android:padding="1dp"
android:src="@drawable/ic_public_room"
android:visibility="gone"
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="20dp"
tools:ignore="MissingConstraints"
tools:visibility="visible" />
<TextView
android:id="@+id/roomToolbarTitleView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toTopOf="@+id/roomToolbarSubtitleView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/roomToolbarDecorationImageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:layout_goneMarginStart="7dp"
tools:text="@sample/rooms.json/data/name" />
<TextView
android:id="@+id/roomToolbarSubtitleView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
app:layout_constraintTop_toBottomOf="@+id/roomToolbarTitleView"
tools:text="@sample/rooms.json/data/topic"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/test"
android:icon="@drawable/ic_search"
android:title="@string/action_thread_view_in_room"
app:showAsAction="always">
<menu>
<item
android:id="@+id/menuWithIconText"
android:icon="@drawable/ic_thread_menu_item"
android:title="@string/action_thread_share"
/>
</menu>
</item>
<item
android:id="@+id/menu_thread_timeline_view_in_room"
android:icon="@drawable/ic_settings_x"
android:title="@string/action_thread_view_in_room"
app:showAsAction="never" />
<item
android:id="@+id/menu_thread_timeline_copy"
android:title="@string/action_thread_copy_link_to_thread"
app:showAsAction="never" />
<item
android:id="@+id/menu_thread_timeline_share"
android:title="@string/action_thread_share"
app:showAsAction="never" />
</menu>

View file

@ -37,11 +37,20 @@
app:showAsAction="always" app:showAsAction="always"
tools:visible="true" /> tools:visible="true" />
<item android:id="@+id/join_conference" <item
android:id="@+id/threads"
android:icon="@drawable/ic_thread_menu_item"
android:title="@string/action_view_threads"
android:visible="false"
app:iconTint="?colorPrimary"
app:showAsAction="always"
tools:visible="true" />
<item
android:id="@+id/join_conference"
android:title="@string/join" android:title="@string/join"
app:actionLayout="@layout/layout_join_conference_action" app:actionLayout="@layout/layout_join_conference_action"
app:showAsAction="always" app:showAsAction="always" />
/>
<item <item
android:id="@+id/open_matrix_apps" android:id="@+id/open_matrix_apps"
@ -57,4 +66,36 @@
app:showAsAction="never" app:showAsAction="never"
tools:visible="true" /> tools:visible="true" />
<item
android:id="@+id/menu_thread_timeline_more"
android:icon="@drawable/ic_more_vertical"
android:title="@string/action_thread_view_in_room"
app:iconTint="?vctr_content_secondary"
app:showAsAction="always">
<menu>
<item
android:id="@+id/menu_thread_timeline_view_in_room"
android:icon="@drawable/ic_thread_view_in_room_menu_item"
android:title="@string/action_thread_view_in_room"
app:iconTint="?vctr_content_secondary"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_thread_timeline_copy_link"
android:icon="@drawable/ic_thread_link_menu_item"
android:title="@string/action_thread_copy_link_to_thread"
app:iconTint="?vctr_content_secondary"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_thread_timeline_share"
android:icon="@drawable/ic_thread_share_menu_item"
android:title="@string/action_thread_share"
app:iconTint="?vctr_content_secondary"
app:showAsAction="ifRoom" />
</menu>
</item>
</menu> </menu>

View file

@ -441,6 +441,7 @@
<string name="action_sign_out_confirmation_simple">Are you sure you want to sign out?</string> <string name="action_sign_out_confirmation_simple">Are you sure you want to sign out?</string>
<string name="action_voice_call">Voice Call</string> <string name="action_voice_call">Voice Call</string>
<string name="action_video_call">Video Call</string> <string name="action_video_call">Video Call</string>
<string name="action_view_threads">View Threads</string>
<string name="action_global_search">Global search</string> <string name="action_global_search">Global search</string>
<string name="action_mark_all_as_read">Mark all as read</string> <string name="action_mark_all_as_read">Mark all as read</string>
<string name="action_historical">Historical</string> <string name="action_historical">Historical</string>
@ -456,6 +457,11 @@
<string name="disable">Disable</string> <string name="disable">Disable</string>
<string name="action_return">Return</string> <string name="action_return">Return</string>
<!-- actions threads -->
<string name="action_thread_view_in_room">View in room</string>
<string name="action_thread_copy_link_to_thread">Copy link to thread</string>
<string name="action_thread_share">Share</string>
<!-- dialog titles --> <!-- dialog titles -->
<string name="dialog_title_confirmation">Confirmation</string> <string name="dialog_title_confirmation">Confirmation</string>
<string name="dialog_title_warning">Warning</string> <string name="dialog_title_warning">Warning</string>
@ -1023,6 +1029,7 @@
<!-- Room Threads --> <!-- Room Threads -->
<string name="room_threads_filter">Filter Threads in room</string> <string name="room_threads_filter">Filter Threads in room</string>
<string name="room_threads_title">Thread</string>
<!-- Room events --> <!-- Room events -->
<string name="room_event_action_report_prompt_reason">Reason for reporting this content</string> <string name="room_event_action_report_prompt_reason">Reason for reporting this content</string>