- Add threads to lab settings

- Disable thread awareness due to the new fallback mechanism
This commit is contained in:
ariskotsomitopoulos 2022-01-19 12:28:00 +02:00
parent 4cff3938e7
commit 8cc96e27bc
23 changed files with 116 additions and 58 deletions

View file

@ -96,6 +96,11 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
companion object { companion object {
/**
* Determines whether or not thread messages are enabled
*/
var areThreadMessagesEnabled: Boolean = false
private lateinit var instance: Matrix private lateinit var instance: Matrix
private val isInit = AtomicBoolean(false) private val isInit = AtomicBoolean(false)

View file

@ -40,7 +40,6 @@ internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded(
roomId: String, roomId: String,
realm: Realm, currentUserId: String, realm: Realm, currentUserId: String,
shouldUpdateNotifications: Boolean = true) { shouldUpdateNotifications: Boolean = true) {
if (!BuildConfig.THREADING_ENABLED) return
for ((rootThreadEventId, eventEntity) in this) { for ((rootThreadEventId, eventEntity) in this) {
eventEntity.findAllThreadsForRootEventId(eventEntity.realm, rootThreadEventId).let { eventEntity.findAllThreadsForRootEventId(eventEntity.realm, rootThreadEventId).let {

View file

@ -295,7 +295,8 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
.orEmpty() .orEmpty()
if (timelineEvents.isEmpty()) return LoadedFromStorage() if (timelineEvents.isEmpty()) return LoadedFromStorage()
fetchRootThreadEventsIfNeeded(timelineEvents) // Disabled due to the new fallback
// fetchRootThreadEventsIfNeeded(timelineEvents)
if (direction == Timeline.Direction.FORWARDS) { if (direction == Timeline.Direction.FORWARDS) {
builtEventsIndexes.entries.forEach { it.setValue(it.value + timelineEvents.size) } builtEventsIndexes.entries.forEach { it.setValue(it.value + timelineEvents.size) }
} }
@ -332,7 +333,7 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
* in order to be able to display the event to the user appropriately * in order to be able to display the event to the user appropriately
*/ */
private suspend fun fetchRootThreadEventsIfNeeded(offsetResults: List<TimelineEventEntity>) { private suspend fun fetchRootThreadEventsIfNeeded(offsetResults: List<TimelineEventEntity>) {
if (BuildConfig.THREADING_ENABLED) return // if (BuildConfig.THREADING_ENABLED) return
val eventEntityList = offsetResults val eventEntityList = offsetResults
.mapNotNull { .mapNotNull {
it.root it.root

View file

@ -116,15 +116,16 @@ internal class TimelineEventDecryptor @Inject constructor(
eventEntity?.apply { eventEntity?.apply {
val decryptedPayload = val decryptedPayload =
if (!BuildConfig.THREADING_ENABLED) { // Disabled due to the new fallback
threadsAwarenessHandler.handleIfNeededDuringDecryption( // if (!BuildConfig.THREADING_ENABLED) {
it, // threadsAwarenessHandler.handleIfNeededDuringDecryption(
roomId = event.roomId, // it,
event, // roomId = event.roomId,
result) // event,
} else { // result)
// } else {
null null
} // }
setDecryptionResult(result, decryptedPayload) setDecryptionResult(result, decryptedPayload)
} }
} }

View file

@ -19,6 +19,8 @@ package org.matrix.android.sdk.internal.session.room.timeline
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import dagger.Lazy import dagger.Lazy
import io.realm.Realm import io.realm.Realm
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixConfiguration
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.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
@ -50,6 +52,7 @@ import javax.inject.Inject
internal class TokenChunkEventPersistor @Inject constructor( internal class TokenChunkEventPersistor @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
@UserId private val userId: String, @UserId private val userId: String,
private val matrixConfiguration: MatrixConfiguration,
private val liveEventManager: Lazy<StreamEventsManager>) { private val liveEventManager: Lazy<StreamEventsManager>) {
enum class Result { enum class Result {
@ -182,18 +185,22 @@ internal class TokenChunkEventPersistor @Inject constructor(
} }
liveEventManager.get().dispatchPaginatedEventReceived(event, roomId) liveEventManager.get().dispatchPaginatedEventReceived(event, roomId)
currentChunk.addTimelineEvent(roomId, eventEntity, direction, roomMemberContentsByUser) currentChunk.addTimelineEvent(roomId, eventEntity, direction, roomMemberContentsByUser)
eventEntity.rootThreadEventId?.let { if(Matrix.areThreadMessagesEnabled) {
// This is a thread event eventEntity.rootThreadEventId?.let {
optimizedThreadSummaryMap[it] = eventEntity // This is a thread event
} ?: run { optimizedThreadSummaryMap[it] = eventEntity
// This is a normal event or a root thread one } ?: run {
optimizedThreadSummaryMap[eventEntity.eventId] = eventEntity // This is a normal event or a root thread one
optimizedThreadSummaryMap[eventEntity.eventId] = eventEntity
}
} }
} }
} }
if (currentChunk.isValid) { if (currentChunk.isValid) {
RoomEntity.where(realm, roomId).findFirst()?.addIfNecessary(currentChunk) RoomEntity.where(realm, roomId).findFirst()?.addIfNecessary(currentChunk)
} }
optimizedThreadSummaryMap.updateThreadSummaryIfNeeded(roomId = roomId, realm = realm, currentUserId = userId) if(Matrix.areThreadMessagesEnabled) {
optimizedThreadSummaryMap.updateThreadSummaryIfNeeded(roomId = roomId, realm = realm, currentUserId = userId)
}
} }
} }

View file

@ -102,9 +102,10 @@ internal class SyncResponseHandler @Inject constructor(
val aggregator = SyncResponsePostTreatmentAggregator() val aggregator = SyncResponsePostTreatmentAggregator()
// Prerequisite for thread events handling in RoomSyncHandler // Prerequisite for thread events handling in RoomSyncHandler
if (!BuildConfig.THREADING_ENABLED) { // Disabled due to the new fallback
threadsAwarenessHandler.fetchRootThreadEventsIfNeeded(syncResponse) // if (!BuildConfig.THREADING_ENABLED) {
} // threadsAwarenessHandler.fetchRootThreadEventsIfNeeded(syncResponse)
// }
// Start one big transaction // Start one big transaction
monarchy.awaitTransaction { realm -> monarchy.awaitTransaction { realm ->

View file

@ -19,7 +19,8 @@ package org.matrix.android.sdk.internal.session.sync.handler.room
import dagger.Lazy import dagger.Lazy
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.createObject import io.realm.kotlin.createObject
import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
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
@ -82,6 +83,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
private val roomMemberEventHandler: RoomMemberEventHandler, private val roomMemberEventHandler: RoomMemberEventHandler,
private val roomTypingUsersHandler: RoomTypingUsersHandler, private val roomTypingUsersHandler: RoomTypingUsersHandler,
private val threadsAwarenessHandler: ThreadsAwarenessHandler, private val threadsAwarenessHandler: ThreadsAwarenessHandler,
private val matrixConfiguration: MatrixConfiguration,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource, private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
@UserId private val userId: String, @UserId private val userId: String,
private val timelineInput: TimelineInput, private val timelineInput: TimelineInput,
@ -379,13 +381,13 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
if (event.isEncrypted() && !isInitialSync) { if (event.isEncrypted() && !isInitialSync) {
decryptIfNeeded(event, roomId) decryptIfNeeded(event, roomId)
} }
// Disabled due to the new fallback
if (!BuildConfig.THREADING_ENABLED) { // if (!BuildConfig.THREADING_ENABLED) {
threadsAwarenessHandler.handleIfNeeded( // threadsAwarenessHandler.handleIfNeeded(
realm = realm, // realm = realm,
roomId = roomId, // roomId = roomId,
event = event) // event = event)
} // }
val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it } val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it }
val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, insertType) val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, insertType)
@ -408,12 +410,14 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
} }
chunkEntity.addTimelineEvent(roomId, eventEntity, PaginationDirection.FORWARDS, roomMemberContentsByUser) chunkEntity.addTimelineEvent(roomId, eventEntity, PaginationDirection.FORWARDS, roomMemberContentsByUser)
eventEntity.rootThreadEventId?.let { if(Matrix.areThreadMessagesEnabled) {
// This is a thread event eventEntity.rootThreadEventId?.let {
optimizedThreadSummaryMap[it] = eventEntity // This is a thread event
} ?: run { optimizedThreadSummaryMap[it] = eventEntity
// This is a normal event or a root thread one } ?: run {
optimizedThreadSummaryMap[eventEntity.eventId] = eventEntity // This is a normal event or a root thread one
optimizedThreadSummaryMap[eventEntity.eventId] = eventEntity
}
} }
// Give info to crypto module // Give info to crypto module
@ -442,10 +446,12 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
} }
// Handle deletion of [stuck] local echos if needed // Handle deletion of [stuck] local echos if needed
deleteLocalEchosIfNeeded(insertType, roomEntity, eventList) deleteLocalEchosIfNeeded(insertType, roomEntity, eventList)
optimizedThreadSummaryMap.updateThreadSummaryIfNeeded( if(Matrix.areThreadMessagesEnabled) {
roomId = roomId, optimizedThreadSummaryMap.updateThreadSummaryIfNeeded(
realm = realm, roomId = roomId,
currentUserId = userId) realm = realm,
currentUserId = userId)
}
// posting new events to timeline if any is registered // posting new events to timeline if any is registered
timelineInput.onNewTimelineEvents(roomId = roomId, eventIds = eventIds) timelineInput.onNewTimelineEvents(roomId = roomId, eventIds = eventIds)

View file

@ -83,6 +83,7 @@ import im.vector.app.features.themes.ThemeUtils
import im.vector.app.receivers.DebugReceiver import im.vector.app.receivers.DebugReceiver
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.failure.GlobalError
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
@ -193,6 +194,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
navigator = singletonEntryPoint.navigator() navigator = singletonEntryPoint.navigator()
activeSessionHolder = singletonEntryPoint.activeSessionHolder() activeSessionHolder = singletonEntryPoint.activeSessionHolder()
vectorPreferences = singletonEntryPoint.vectorPreferences() vectorPreferences = singletonEntryPoint.vectorPreferences()
Matrix.areThreadMessagesEnabled = vectorPreferences.areThreadMessagesEnabled()
configurationViewModel.activityRestarter.observe(this) { configurationViewModel.activityRestarter.observe(this) {
if (!it.hasBeenHandled) { if (!it.hasBeenHandled) {
// Recreate the Activity because configuration has changed // Recreate the Activity because configuration has changed

View file

@ -52,4 +52,8 @@ class UserPreferencesProvider @Inject constructor(private val vectorPreferences:
fun shouldShowPolls(): Boolean { fun shouldShowPolls(): Boolean {
return vectorPreferences.labsEnablePolls() return vectorPreferences.labsEnablePolls()
} }
fun areThreadMessagesEnabled(): Boolean {
return vectorPreferences.areThreadMessagesEnabled()
}
} }

View file

@ -57,7 +57,7 @@ class AutocompleteCommandPresenter @AssistedInject constructor(
!it.isDevCommand || vectorPreferences.developerMode() !it.isDevCommand || vectorPreferences.developerMode()
} }
.filter { .filter {
if (BuildConfig.THREADING_ENABLED && isInThreadTimeline) { if (vectorPreferences.areThreadMessagesEnabled() && isInThreadTimeline) {
it.isThreadCommand it.isThreadCommand
} else { } else {
true true

View file

@ -65,7 +65,7 @@ object CommandParser {
val slashCommand = messageParts.first() val slashCommand = messageParts.first()
val message = textMessage.substring(slashCommand.length).trim() val message = textMessage.substring(slashCommand.length).trim()
if (BuildConfig.THREADING_ENABLED && isInThreadTimeline) { if (isInThreadTimeline) {
val notSupportedCommandsInThreads = Command.values().filter { val notSupportedCommandsInThreads = Command.values().filter {
!it.isThreadCommand !it.isThreadCommand
}.map { }.map {

View file

@ -1958,7 +1958,7 @@ class TimelineFragment @Inject constructor(
} }
override fun onThreadSummaryClicked(eventId: String, isRootThreadEvent: Boolean) { override fun onThreadSummaryClicked(eventId: String, isRootThreadEvent: Boolean) {
if (BuildConfig.THREADING_ENABLED && isRootThreadEvent && !isThreadTimeLine()) { if (vectorPreferences.areThreadMessagesEnabled() && isRootThreadEvent && !isThreadTimeLine()) {
navigateToThreadTimeline(eventId) navigateToThreadTimeline(eventId)
} }
} }

View file

@ -695,7 +695,7 @@ class TimelineViewModel @AssistedInject constructor(
// Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^ // 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.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
R.id.search -> true R.id.search -> true
R.id.menu_timeline_thread_list -> BuildConfig.THREADING_ENABLED R.id.menu_timeline_thread_list -> vectorPreferences.areThreadMessagesEnabled()
R.id.dev_tools -> vectorPreferences.developerMode() R.id.dev_tools -> vectorPreferences.developerMode()
else -> false else -> false
} }

View file

@ -30,6 +30,7 @@ import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.epoxy.noResultItem
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.core.ui.list.GenericHeaderItem_ import im.vector.app.core.ui.list.GenericHeaderItem_
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
@ -43,7 +44,8 @@ class SearchResultController @Inject constructor(
private val session: Session, private val session: Session,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val dateFormatter: VectorDateFormatter private val dateFormatter: VectorDateFormatter,
private val userPreferencesProvider: UserPreferencesProvider
) : TypedEpoxyController<SearchViewState>() { ) : TypedEpoxyController<SearchViewState>() {
var listener: Listener? = null var listener: Listener? = null
@ -123,6 +125,7 @@ class SearchResultController @Inject constructor(
.sender(eventAndSender.sender .sender(eventAndSender.sender
?: eventAndSender.event.senderId?.let { session.getRoomMember(it, data.roomId) }?.toMatrixItem()) ?: eventAndSender.event.senderId?.let { session.getRoomMember(it, data.roomId) }?.toMatrixItem())
.threadDetails(event.threadDetails) .threadDetails(event.threadDetails)
.areThreadMessagesEnabled(userPreferencesProvider.areThreadMessagesEnabled())
.listener { listener?.onItemClicked(eventAndSender.event) } .listener { listener?.onItemClicked(eventAndSender.event) }
.let { result.add(it) } .let { result.add(it) }
} }

View file

@ -43,6 +43,7 @@ abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
@EpoxyAttribute lateinit var spannable: EpoxyCharSequence @EpoxyAttribute lateinit var spannable: EpoxyCharSequence
@EpoxyAttribute var sender: MatrixItem? = null @EpoxyAttribute var sender: MatrixItem? = null
@EpoxyAttribute var threadDetails: ThreadDetails? = null @EpoxyAttribute var threadDetails: ThreadDetails? = null
@EpoxyAttribute var areThreadMessagesEnabled: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
@ -55,7 +56,7 @@ abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
holder.timeView.text = formattedDate holder.timeView.text = formattedDate
holder.contentView.text = spannable.charSequence holder.contentView.text = spannable.charSequence
if (BuildConfig.THREADING_ENABLED) { if (areThreadMessagesEnabled) {
threadDetails?.let { threadDetails?.let {
if (it.isRootThread) { if (it.isRootThread) {
showThreadSummary(holder) showThreadSummary(holder)

View file

@ -447,7 +447,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
private fun canReplyInThread(event: TimelineEvent, private fun canReplyInThread(event: TimelineEvent,
messageContent: MessageContent?, messageContent: MessageContent?,
actionPermissions: ActionPermissions): Boolean { actionPermissions: ActionPermissions): Boolean {
if (!BuildConfig.THREADING_ENABLED) return false if (!vectorPreferences.areThreadMessagesEnabled()) return false
if (initialState.isFromThreadTimeline) return false if (initialState.isFromThreadTimeline) return false
if (event.root.getClearType() != EventType.MESSAGE && if (event.root.getClearType() != EventType.MESSAGE &&
!event.isSticker() && !event.isPoll()) return false !event.isSticker() && !event.isPoll()) return false
@ -472,7 +472,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
private fun canViewInRoom(event: TimelineEvent, private fun canViewInRoom(event: TimelineEvent,
messageContent: MessageContent?, messageContent: MessageContent?,
actionPermissions: ActionPermissions): Boolean { actionPermissions: ActionPermissions): Boolean {
if (!BuildConfig.THREADING_ENABLED) return false if (!vectorPreferences.areThreadMessagesEnabled()) return false
if (!initialState.isFromThreadTimeline) return false if (!initialState.isFromThreadTimeline) return false
if (event.root.getClearType() != EventType.MESSAGE && if (event.root.getClearType() != EventType.MESSAGE &&
!event.isSticker() && !event.isPoll()) return false !event.isSticker() && !event.isPoll()) return false

View file

@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.helper
import im.vector.app.EmojiCompatFontProvider import im.vector.app.EmojiCompatFontProvider
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.TimelineEventController
@ -31,6 +32,7 @@ class MessageItemAttributesFactory @Inject constructor(
private val messageColorProvider: MessageColorProvider, private val messageColorProvider: MessageColorProvider,
private val avatarSizeProvider: AvatarSizeProvider, private val avatarSizeProvider: AvatarSizeProvider,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val preferencesProvider: UserPreferencesProvider,
private val emojiCompatFontProvider: EmojiCompatFontProvider) { private val emojiCompatFontProvider: EmojiCompatFontProvider) {
fun create(messageContent: Any?, fun create(messageContent: Any?,
@ -57,7 +59,8 @@ class MessageItemAttributesFactory @Inject constructor(
readReceiptsCallback = callback, readReceiptsCallback = callback,
emojiTypeFace = emojiCompatFontProvider.typeface, emojiTypeFace = emojiCompatFontProvider.typeface,
decryptionErrorMessage = stringProvider.getString(R.string.encrypted_message), decryptionErrorMessage = stringProvider.getString(R.string.encrypted_message),
threadDetails = threadDetails threadDetails = threadDetails,
areThreadMessagesEnabled = preferencesProvider.areThreadMessagesEnabled()
) )
} }
} }

View file

@ -152,12 +152,11 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
return true return true
} }
if (BuildConfig.THREADING_ENABLED && !isFromThreadTimeline && root.isThread()) { if (userPreferencesProvider.areThreadMessagesEnabled() && !isFromThreadTimeline && root.isThread()) {
return true return true
} }
if (BuildConfig.THREADING_ENABLED && isFromThreadTimeline) { if (userPreferencesProvider.areThreadMessagesEnabled() && isFromThreadTimeline) {
// //
return if (root.getRootThreadEventId() == rootThreadEventId) { return if (root.getRootThreadEventId() == rootThreadEventId) {
false false
} else root.eventId != rootThreadEventId } else root.eventId != rootThreadEventId

View file

@ -113,7 +113,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
holder.eventSendingIndicator.isVisible = attributes.informationData.sendStateDecoration == SendStateDecoration.SENDING_MEDIA holder.eventSendingIndicator.isVisible = attributes.informationData.sendStateDecoration == SendStateDecoration.SENDING_MEDIA
// Threads // Threads
if (BuildConfig.THREADING_ENABLED) { if (attributes.areThreadMessagesEnabled) {
holder.threadSummaryConstraintLayout.onClick(_threadClickListener) holder.threadSummaryConstraintLayout.onClick(_threadClickListener)
attributes.threadDetails?.let { threadDetails -> attributes.threadDetails?.let { threadDetails ->
holder.threadSummaryConstraintLayout.isVisible = threadDetails.isRootThread holder.threadSummaryConstraintLayout.isVisible = threadDetails.isRootThread
@ -186,7 +186,8 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null, override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
val emojiTypeFace: Typeface? = null, val emojiTypeFace: Typeface? = null,
val decryptionErrorMessage: String? = null, val decryptionErrorMessage: String? = null,
val threadDetails: ThreadDetails? = null val threadDetails: ThreadDetails? = null,
val areThreadMessagesEnabled: Boolean = false
) : AbsBaseMessageItem.Attributes { ) : AbsBaseMessageItem.Attributes {
// Have to override as it's used to diff epoxy items // Have to override as it's used to diff epoxy items

View file

@ -197,6 +197,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
private const val TAKE_PHOTO_VIDEO_MODE = "TAKE_PHOTO_VIDEO_MODE" private const val TAKE_PHOTO_VIDEO_MODE = "TAKE_PHOTO_VIDEO_MODE"
private const val SETTINGS_LABS_ENABLE_POLLS = "SETTINGS_LABS_ENABLE_POLLS" private const val SETTINGS_LABS_ENABLE_POLLS = "SETTINGS_LABS_ENABLE_POLLS"
const val SETTINGS_LABS_ENABLE_THREAD_MESSAGES = "SETTINGS_LABS_ENABLE_THREAD_MESSAGES"
// Possible values for TAKE_PHOTO_VIDEO_MODE // Possible values for TAKE_PHOTO_VIDEO_MODE
const val TAKE_PHOTO_VIDEO_MODE_ALWAYS_ASK = 0 const val TAKE_PHOTO_VIDEO_MODE_ALWAYS_ASK = 0
@ -995,4 +996,8 @@ class VectorPreferences @Inject constructor(private val context: Context) {
fun labsEnablePolls(): Boolean { fun labsEnablePolls(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_POLLS, false) return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_POLLS, false)
} }
fun areThreadMessagesEnabled(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_THREAD_MESSAGES, false)
}
} }

View file

@ -16,8 +16,11 @@
package im.vector.app.features.settings package im.vector.app.features.settings
import androidx.preference.Preference
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.core.preference.VectorSwitchPreference
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
import javax.inject.Inject import javax.inject.Inject
class VectorSettingsLabsFragment @Inject constructor( class VectorSettingsLabsFragment @Inject constructor(
@ -32,5 +35,15 @@ class VectorSettingsLabsFragment @Inject constructor(
// ensure correct default // ensure correct default
pref.isChecked = vectorPreferences.labsAutoReportUISI() pref.isChecked = vectorPreferences.labsAutoReportUISI()
} }
// clear cache
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_ENABLE_THREAD_MESSAGES)?.let {
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
displayLoadingView()
MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCache = true))
false
}
}
} }
} }

View file

@ -3600,6 +3600,8 @@
<string name="labs_auto_report_uisi">Auto Report Decryption Errors.</string> <string name="labs_auto_report_uisi">Auto Report Decryption Errors.</string>
<string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string> <string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string>
<string name="labs_enable_thread_messages">Enable Thread Messages</string>
<string name="labs_enable_thread_messages_desc">Note: app will be restarted</string>
<string name="user_invites_you">%s invites you</string> <string name="user_invites_you">%s invites you</string>

View file

@ -56,11 +56,16 @@
android:key="SETTINGS_LABS_ENABLE_POLLS" android:key="SETTINGS_LABS_ENABLE_POLLS"
android:title="@string/labs_enable_polls" /> android:title="@string/labs_enable_polls" />
<im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false"
android:key="SETTINGS_LABS_ENABLE_THREAD_MESSAGES"
android:summary="@string/labs_enable_thread_messages_desc"
android:title="@string/labs_enable_thread_messages" />
<im.vector.app.core.preference.VectorSwitchPreference <im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false" android:defaultValue="false"
android:key="SETTINGS_LABS_AUTO_REPORT_UISI" android:key="SETTINGS_LABS_AUTO_REPORT_UISI"
android:title="@string/labs_auto_report_uisi" android:summary="@string/labs_auto_report_uisi_desc"
android:summary="@string/labs_auto_report_uisi_desc"/> android:title="@string/labs_auto_report_uisi" />
</androidx.preference.PreferenceScreen> </androidx.preference.PreferenceScreen>