Add analytics to threads
This commit is contained in:
parent
eda723c230
commit
e59f2bba0a
|
@ -17,6 +17,7 @@
|
||||||
package org.matrix.android.sdk.api.session.room.timeline
|
package org.matrix.android.sdk.api.session.room.timeline
|
||||||
|
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import org.matrix.android.sdk.BuildConfig
|
||||||
|
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.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||||
|
@ -159,6 +160,13 @@ fun TimelineEvent.isSticker(): Boolean {
|
||||||
return root.isSticker()
|
return root.isSticker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the event is a root thread event
|
||||||
|
*/
|
||||||
|
fun TimelineEvent.isRootThread(): Boolean {
|
||||||
|
return root.threadDetails?.isRootThread.orFalse()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the latest message body, after a possible edition, stripping the reply prefix if necessary
|
* Get the latest message body, after a possible edition, stripping the reply prefix if necessary
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.analytics.extensions
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
|
import im.vector.app.features.home.room.detail.composer.MessageComposerViewState
|
||||||
|
import im.vector.app.features.home.room.detail.composer.SendMode
|
||||||
|
|
||||||
|
fun MessageComposerViewState.toAnalyticsComposer(): Composer =
|
||||||
|
Composer(
|
||||||
|
inThread = isInThreadTimeline(),
|
||||||
|
isEditing = sendMode is SendMode.Edit,
|
||||||
|
isReply = sendMode is SendMode.Reply,
|
||||||
|
startsThread = startsThread)
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.analytics.extensions
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.plan.Interaction
|
||||||
|
|
||||||
|
fun Interaction.Name.toAnalyticsInteraction(interactionType: Interaction.InteractionType = Interaction.InteractionType.Touch) =
|
||||||
|
Interaction(
|
||||||
|
name = this,
|
||||||
|
interactionType = interactionType)
|
|
@ -39,6 +39,10 @@ data class Composer(
|
||||||
* sent event.
|
* sent event.
|
||||||
*/
|
*/
|
||||||
val isReply: Boolean,
|
val isReply: Boolean,
|
||||||
|
/**
|
||||||
|
* Whether this message begins a new thread or not.
|
||||||
|
*/
|
||||||
|
val startsThread: Boolean? = null,
|
||||||
) : VectorAnalyticsEvent {
|
) : VectorAnalyticsEvent {
|
||||||
|
|
||||||
override fun getName() = "Composer"
|
override fun getName() = "Composer"
|
||||||
|
@ -48,6 +52,7 @@ data class Composer(
|
||||||
put("inThread", inThread)
|
put("inThread", inThread)
|
||||||
put("isEditing", isEditing)
|
put("isEditing", isEditing)
|
||||||
put("isReply", isReply)
|
put("isReply", isReply)
|
||||||
|
startsThread?.let { put("startsThread", it) }
|
||||||
}.takeIf { it.isNotEmpty() }
|
}.takeIf { it.isNotEmpty() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,36 @@ data class Interaction(
|
||||||
) : VectorAnalyticsEvent {
|
) : VectorAnalyticsEvent {
|
||||||
|
|
||||||
enum class Name {
|
enum class Name {
|
||||||
|
/**
|
||||||
|
* User tapped on Add to Home button on Room Details screen.
|
||||||
|
*/
|
||||||
|
MobileRoomAddHome,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User tapped on Leave Room button on Room Details screen.
|
||||||
|
*/
|
||||||
|
MobileRoomLeave,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User tapped on Threads button on Room screen.
|
||||||
|
*/
|
||||||
|
MobileRoomThreadListButton,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User tapped on a thread summary item on Room screen.
|
||||||
|
*/
|
||||||
|
MobileRoomThreadSummaryItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User tapped on the filter button on ThreadList screen.
|
||||||
|
*/
|
||||||
|
MobileThreadListFilterItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User selected a thread on ThreadList screen.
|
||||||
|
*/
|
||||||
|
MobileThreadListThreadItem,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User tapped the already selected space from the space list.
|
* User tapped the already selected space from the space list.
|
||||||
*/
|
*/
|
||||||
|
@ -52,8 +82,8 @@ data class Interaction(
|
||||||
SpacePanelSwitchSpace,
|
SpacePanelSwitchSpace,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User clicked the create room button in the + context menu of the room
|
* User clicked the create room button in the add existing room to space
|
||||||
* list header in Element Web/Desktop.
|
* dialog in Element Web/Desktop.
|
||||||
*/
|
*/
|
||||||
WebAddExistingToSpaceDialogCreateRoomButton,
|
WebAddExistingToSpaceDialogCreateRoomButton,
|
||||||
|
|
||||||
|
@ -105,12 +135,24 @@ data class Interaction(
|
||||||
*/
|
*/
|
||||||
WebRightPanelRoomUserInfoInviteButton,
|
WebRightPanelRoomUserInfoInviteButton,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the threads 'show' filter dropdown in the threads panel
|
||||||
|
* in Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebRightPanelThreadPanelFilterDropdown,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User clicked the create room button in the room directory of Element
|
* User clicked the create room button in the room directory of Element
|
||||||
* Web/Desktop.
|
* Web/Desktop.
|
||||||
*/
|
*/
|
||||||
WebRoomDirectoryCreateRoomButton,
|
WebRoomDirectoryCreateRoomButton,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the Threads button in the top right of a room in Element
|
||||||
|
* Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebRoomHeaderButtonsThreadsButton,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User adjusted their favourites using the context menu on the header
|
* User adjusted their favourites using the context menu on the header
|
||||||
* of a room in Element Web/Desktop.
|
* of a room in Element Web/Desktop.
|
||||||
|
@ -153,6 +195,12 @@ data class Interaction(
|
||||||
*/
|
*/
|
||||||
WebRoomListHeaderPlusMenuCreateRoomItem,
|
WebRoomListHeaderPlusMenuCreateRoomItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the explore rooms button in the + context menu of the
|
||||||
|
* room list header in Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebRoomListHeaderPlusMenuExploreRoomsItem,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User adjusted their favourites using the context menu on a room tile
|
* User adjusted their favourites using the context menu on a room tile
|
||||||
* in the room list in Element Web/Desktop.
|
* in the room list in Element Web/Desktop.
|
||||||
|
@ -189,6 +237,12 @@ data class Interaction(
|
||||||
*/
|
*/
|
||||||
WebRoomListRoomsSublistPlusMenuCreateRoomItem,
|
WebRoomListRoomsSublistPlusMenuCreateRoomItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the explore rooms button in the + context menu of the
|
||||||
|
* rooms sublist in Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebRoomListRoomsSublistPlusMenuExploreRoomsItem,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User interacted with leave action in the general tab of the room
|
* User interacted with leave action in the general tab of the room
|
||||||
* settings dialog in Element Web/Desktop.
|
* settings dialog in Element Web/Desktop.
|
||||||
|
@ -201,6 +255,12 @@ data class Interaction(
|
||||||
*/
|
*/
|
||||||
WebRoomSettingsSecurityTabCreateNewRoomButton,
|
WebRoomSettingsSecurityTabCreateNewRoomButton,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked a thread summary in the timeline of a room in Element
|
||||||
|
* Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebRoomTimelineThreadSummaryButton,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User interacted with the theme radio selector in the Appearance tab
|
* User interacted with the theme radio selector in the Appearance tab
|
||||||
* of Settings in Element Web/Desktop.
|
* of Settings in Element Web/Desktop.
|
||||||
|
@ -214,17 +274,40 @@ data class Interaction(
|
||||||
WebSettingsSidebarTabSpacesCheckbox,
|
WebSettingsSidebarTabSpacesCheckbox,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User clicked the create room button in the + context menu of the room
|
* User clicked the explore rooms button in the context menu of a space
|
||||||
* list header in Element Web/Desktop.
|
* in Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebSpaceContextMenuExploreRoomsItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the home button in the context menu of a space in
|
||||||
|
* Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebSpaceContextMenuHomeItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the new room button in the context menu of a space in
|
||||||
|
* Element Web/Desktop.
|
||||||
*/
|
*/
|
||||||
WebSpaceContextMenuNewRoomItem,
|
WebSpaceContextMenuNewRoomItem,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User clicked the create room button in the + context menu of the room
|
* User clicked the new room button in the context menu on the space
|
||||||
* list header in Element Web/Desktop.
|
* home in Element Web/Desktop.
|
||||||
*/
|
*/
|
||||||
WebSpaceHomeCreateRoomButton,
|
WebSpaceHomeCreateRoomButton,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the back button on a Thread view going back to the
|
||||||
|
* Threads Panel of Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebThreadViewBackButton,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User selected a thread in the Threads panel in Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebThreadsPanelThreadItem,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User clicked the theme toggle button in the user menu of Element
|
* User clicked the theme toggle button in the user menu of Element
|
||||||
* Web/Desktop.
|
* Web/Desktop.
|
||||||
|
|
|
@ -225,6 +225,11 @@ data class MobileScreen(
|
||||||
*/
|
*/
|
||||||
SwitchDirectory,
|
SwitchDirectory,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screen that displays list of threads for a room
|
||||||
|
*/
|
||||||
|
ThreadList,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A screen that shows information about a room member.
|
* A screen that shows information about a room member.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -119,7 +119,8 @@ import im.vector.app.core.utils.startInstallFromSourceIntent
|
||||||
import im.vector.app.core.utils.toast
|
import im.vector.app.core.utils.toast
|
||||||
import im.vector.app.databinding.DialogReportContentBinding
|
import im.vector.app.databinding.DialogReportContentBinding
|
||||||
import im.vector.app.databinding.FragmentTimelineBinding
|
import im.vector.app.databinding.FragmentTimelineBinding
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.extensions.toAnalyticsInteraction
|
||||||
|
import im.vector.app.features.analytics.plan.Interaction
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import im.vector.app.features.attachments.AttachmentTypeSelectorView
|
import im.vector.app.features.attachments.AttachmentTypeSelectorView
|
||||||
import im.vector.app.features.attachments.AttachmentsHelper
|
import im.vector.app.features.attachments.AttachmentsHelper
|
||||||
|
@ -1505,9 +1506,6 @@ class TimelineFragment @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (text.isNotBlank()) {
|
if (text.isNotBlank()) {
|
||||||
withState(messageComposerViewModel) { state ->
|
|
||||||
analyticsTracker.capture(Composer(isThreadTimeLine(), isEditing = state.sendMode is SendMode.Edit, isReply = state.sendMode is SendMode.Reply))
|
|
||||||
}
|
|
||||||
// We collapse ASAP, if not there will be a slight annoying delay
|
// We collapse ASAP, if not there will be a slight annoying delay
|
||||||
views.composerLayout.collapse(true)
|
views.composerLayout.collapse(true)
|
||||||
lockSendButton = true
|
lockSendButton = true
|
||||||
|
@ -2204,7 +2202,7 @@ class TimelineFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
is EventSharedAction.ReplyInThread -> {
|
is EventSharedAction.ReplyInThread -> {
|
||||||
if (withState(messageComposerViewModel) { it.isVoiceMessageIdle }) {
|
if (withState(messageComposerViewModel) { it.isVoiceMessageIdle }) {
|
||||||
navigateToThreadTimeline(action.eventId)
|
navigateToThreadTimeline(action.eventId, action.startsThread)
|
||||||
} else {
|
} else {
|
||||||
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
|
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
|
||||||
}
|
}
|
||||||
|
@ -2363,9 +2361,11 @@ class TimelineFragment @Inject constructor(
|
||||||
* using the ThreadsActivity
|
* using the ThreadsActivity
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private fun navigateToThreadTimeline(rootThreadEventId: String) {
|
private fun navigateToThreadTimeline(rootThreadEventId: String, startsThread: Boolean = false) {
|
||||||
|
analyticsTracker.capture(Interaction.Name.MobileRoomThreadSummaryItem.toAnalyticsInteraction())
|
||||||
context?.let {
|
context?.let {
|
||||||
val roomThreadDetailArgs = ThreadTimelineArgs(
|
val roomThreadDetailArgs = ThreadTimelineArgs(
|
||||||
|
startsThread = startsThread,
|
||||||
roomId = timelineArgs.roomId,
|
roomId = timelineArgs.roomId,
|
||||||
displayName = timelineViewModel.getRoomSummary()?.displayName,
|
displayName = timelineViewModel.getRoomSummary()?.displayName,
|
||||||
avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl,
|
avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl,
|
||||||
|
@ -2381,6 +2381,7 @@ class TimelineFragment @Inject constructor(
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private fun navigateToThreadList() {
|
private fun navigateToThreadList() {
|
||||||
|
analyticsTracker.capture(Interaction.Name.MobileRoomThreadListButton.toAnalyticsInteraction())
|
||||||
context?.let {
|
context?.let {
|
||||||
val roomThreadDetailArgs = ThreadTimelineArgs(
|
val roomThreadDetailArgs = ThreadTimelineArgs(
|
||||||
roomId = timelineArgs.roomId,
|
roomId = timelineArgs.roomId,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.app.core.extensions.exhaustive
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.analytics.AnalyticsTracker
|
import im.vector.app.features.analytics.AnalyticsTracker
|
||||||
|
import im.vector.app.features.analytics.extensions.toAnalyticsComposer
|
||||||
import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
|
import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
|
||||||
import im.vector.app.features.attachments.toContentAttachmentData
|
import im.vector.app.features.attachments.toContentAttachmentData
|
||||||
import im.vector.app.features.command.CommandParser
|
import im.vector.app.features.command.CommandParser
|
||||||
|
@ -188,6 +189,9 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private fun handleSendMessage(action: MessageComposerAction.SendMessage) {
|
private fun handleSendMessage(action: MessageComposerAction.SendMessage) {
|
||||||
withState { state ->
|
withState { state ->
|
||||||
|
analyticsTracker.capture(state.toAnalyticsComposer()).also {
|
||||||
|
setState { copy(startsThread = false) }
|
||||||
|
}
|
||||||
when (state.sendMode) {
|
when (state.sendMode) {
|
||||||
is SendMode.Regular -> {
|
is SendMode.Regular -> {
|
||||||
when (val slashCommandResult = commandParser.parseSlashCommand(
|
when (val slashCommandResult = commandParser.parseSlashCommand(
|
||||||
|
|
|
@ -19,6 +19,7 @@ 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.arguments.TimelineArgs
|
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
|
||||||
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
|
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,6 +63,7 @@ data class MessageComposerViewState(
|
||||||
val canSendMessage: CanSendStatus = CanSendStatus.Allowed,
|
val canSendMessage: CanSendStatus = CanSendStatus.Allowed,
|
||||||
val isSendButtonVisible: Boolean = false,
|
val isSendButtonVisible: Boolean = false,
|
||||||
val rootThreadEventId: String? = null,
|
val rootThreadEventId: String? = null,
|
||||||
|
val startsThread: Boolean = false,
|
||||||
val sendMode: SendMode = SendMode.Regular("", false),
|
val sendMode: SendMode = SendMode.Regular("", false),
|
||||||
val voiceRecordingUiState: VoiceMessageRecorderView.RecordingUiState = VoiceMessageRecorderView.RecordingUiState.Idle
|
val voiceRecordingUiState: VoiceMessageRecorderView.RecordingUiState = VoiceMessageRecorderView.RecordingUiState.Idle
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
|
@ -80,6 +82,7 @@ data class MessageComposerViewState(
|
||||||
|
|
||||||
constructor(args: TimelineArgs) : this(
|
constructor(args: TimelineArgs) : this(
|
||||||
roomId = args.roomId,
|
roomId = args.roomId,
|
||||||
|
startsThread = args.threadTimelineArgs?.startsThread.orFalse(),
|
||||||
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId)
|
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId)
|
||||||
|
|
||||||
fun isInThreadTimeline(): Boolean = rootThreadEventId != null
|
fun isInThreadTimeline(): Boolean = rootThreadEventId != null
|
||||||
|
|
|
@ -48,7 +48,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int,
|
||||||
data class Reply(val eventId: String) :
|
data class Reply(val eventId: String) :
|
||||||
EventSharedAction(R.string.reply, R.drawable.ic_reply)
|
EventSharedAction(R.string.reply, R.drawable.ic_reply)
|
||||||
|
|
||||||
data class ReplyInThread(val eventId: String) :
|
data class ReplyInThread(val eventId: String, val startsThread: Boolean) :
|
||||||
EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread)
|
EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread)
|
||||||
|
|
||||||
object ViewInRoom :
|
object ViewInRoom :
|
||||||
|
|
|
@ -61,6 +61,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
|
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.isPoll
|
import org.matrix.android.sdk.api.session.room.timeline.isPoll
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.isRootThread
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.isSticker
|
import org.matrix.android.sdk.api.session.room.timeline.isSticker
|
||||||
import org.matrix.android.sdk.flow.flow
|
import org.matrix.android.sdk.flow.flow
|
||||||
import org.matrix.android.sdk.flow.unwrap
|
import org.matrix.android.sdk.flow.unwrap
|
||||||
|
@ -328,7 +329,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canReplyInThread(timelineEvent, messageContent, actionPermissions)) {
|
if (canReplyInThread(timelineEvent, messageContent, actionPermissions)) {
|
||||||
add(EventSharedAction.ReplyInThread(eventId))
|
add(EventSharedAction.ReplyInThread(eventId, !timelineEvent.isRootThread()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canViewInRoom(timelineEvent, messageContent, actionPermissions)) {
|
if (canViewInRoom(timelineEvent, messageContent, actionPermissions)) {
|
||||||
|
|
|
@ -26,6 +26,8 @@ import im.vector.app.core.extensions.addFragmentToBackstack
|
||||||
import im.vector.app.core.extensions.replaceFragment
|
import im.vector.app.core.extensions.replaceFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.app.databinding.ActivityThreadsBinding
|
import im.vector.app.databinding.ActivityThreadsBinding
|
||||||
|
import im.vector.app.features.analytics.extensions.toAnalyticsInteraction
|
||||||
|
import im.vector.app.features.analytics.plan.Interaction
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.room.detail.TimelineFragment
|
import im.vector.app.features.home.room.detail.TimelineFragment
|
||||||
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
|
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
|
||||||
|
@ -92,6 +94,7 @@ class ThreadsActivity : VectorBaseActivity<ActivityThreadsBinding>() {
|
||||||
* One usage of that is from the Threads Activity
|
* One usage of that is from the Threads Activity
|
||||||
*/
|
*/
|
||||||
fun navigateToThreadTimeline(threadTimelineArgs: ThreadTimelineArgs) {
|
fun navigateToThreadTimeline(threadTimelineArgs: ThreadTimelineArgs) {
|
||||||
|
analyticsTracker.capture(Interaction.Name.MobileThreadListThreadItem.toAnalyticsInteraction())
|
||||||
val commonOption: (FragmentTransaction) -> Unit = {
|
val commonOption: (FragmentTransaction) -> Unit = {
|
||||||
it.setCustomAnimations(
|
it.setCustomAnimations(
|
||||||
R.anim.animation_slide_in_right,
|
R.anim.animation_slide_in_right,
|
||||||
|
|
|
@ -26,5 +26,6 @@ data class ThreadTimelineArgs(
|
||||||
val displayName: String?,
|
val displayName: String?,
|
||||||
val avatarUrl: String?,
|
val avatarUrl: String?,
|
||||||
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel?,
|
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel?,
|
||||||
val rootThreadEventId: String? = null
|
val rootThreadEventId: String? = null,
|
||||||
|
val startsThread: Boolean = false
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
|
@ -25,6 +25,9 @@ import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.core.platform.EmptyAction
|
import im.vector.app.core.platform.EmptyAction
|
||||||
import im.vector.app.core.platform.EmptyViewEvents
|
import im.vector.app.core.platform.EmptyViewEvents
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.analytics.AnalyticsTracker
|
||||||
|
import im.vector.app.features.analytics.extensions.toAnalyticsInteraction
|
||||||
|
import im.vector.app.features.analytics.plan.Interaction
|
||||||
import im.vector.app.features.home.room.threads.list.views.ThreadListFragment
|
import im.vector.app.features.home.room.threads.list.views.ThreadListFragment
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
@ -34,6 +37,7 @@ import org.matrix.android.sdk.api.session.threads.ThreadTimelineEvent
|
||||||
import org.matrix.android.sdk.flow.flow
|
import org.matrix.android.sdk.flow.flow
|
||||||
|
|
||||||
class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState: ThreadListViewState,
|
class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState: ThreadListViewState,
|
||||||
|
private val analyticsTracker: AnalyticsTracker,
|
||||||
private val session: Session) :
|
private val session: Session) :
|
||||||
VectorViewModel<ThreadListViewState, EmptyAction, EmptyViewEvents>(initialState) {
|
VectorViewModel<ThreadListViewState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@ -113,9 +117,10 @@ class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canHomeserverUseThreading() = session.getHomeServerCapabilities().canUseThreading
|
fun canHomeserverUseThreading() = session.getHomeServerCapabilities().canUseThreading
|
||||||
|
|
||||||
fun applyFiltering(shouldFilterThreads: Boolean) {
|
fun applyFiltering(shouldFilterThreads: Boolean) {
|
||||||
|
analyticsTracker.capture(Interaction.Name.MobileThreadListFilterItem.toAnalyticsInteraction())
|
||||||
setState {
|
setState {
|
||||||
copy(shouldFilterThreads = shouldFilterThreads)
|
copy(shouldFilterThreads = shouldFilterThreads)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.databinding.FragmentThreadListBinding
|
import im.vector.app.databinding.FragmentThreadListBinding
|
||||||
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.room.detail.timeline.animation.TimelineItemAnimator
|
import im.vector.app.features.home.room.detail.timeline.animation.TimelineItemAnimator
|
||||||
import im.vector.app.features.home.room.threads.ThreadsActivity
|
import im.vector.app.features.home.room.threads.ThreadsActivity
|
||||||
|
@ -62,6 +63,7 @@ class ThreadListFragment @Inject constructor(
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
analyticsScreenName = MobileScreen.ScreenName.ThreadList
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
|
Loading…
Reference in New Issue