Skip to content

Commit

Permalink
Merge pull request #2190 from vector-im/feature/utd_pagination
Browse files Browse the repository at this point in the history
Feature/utd pagination
  • Loading branch information
bmarty authored Sep 30, 2020
2 parents c09d308 + 881ebff commit 2a21871
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 229 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Changes in Element 1.0.9 (2020-XX-XX)
===================================================

Features ✨:
-
- Hide encrypted history (before user is invited). Can be shown if wanted in developer settings

Improvements 🙌:
- Wording differentiation for direct rooms (#2176)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.media.VideoContentRenderer
import im.vector.app.features.settings.VectorPreferences
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.toModel
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.message.MessageImageInfoContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.session.room.timeline.Timeline
Expand All @@ -59,11 +65,13 @@ import javax.inject.Inject
private const val DEFAULT_PREFETCH_THRESHOLD = 30

class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter,
private val vectorPreferences: VectorPreferences,
private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder,
private val timelineItemFactory: TimelineItemFactory,
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val mergedHeaderItemFactory: MergedHeaderItemFactory,
private val session: Session,
@TimelineEventControllerHandler
private val backgroundHandler: Handler
) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor {
Expand Down Expand Up @@ -115,6 +123,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
private val modelCache = arrayListOf<CacheItemData?>()
private var currentSnapshot: List<TimelineEvent> = emptyList()
private var inSubmitList: Boolean = false
private var hasReachedInvite: Boolean = false
private var hasUTD: Boolean = false
private var unreadState: UnreadState = UnreadState.Unknown
private var positionOfReadMarker: Int? = null
private var eventIdToHighlight: String? = null
Expand Down Expand Up @@ -267,7 +277,9 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec

val timelineModels = getModels()
add(timelineModels)

if (hasReachedInvite && hasUTD) {
return
}
// Avoid displaying two loaders if there is no elements between them
val showBackwardsLoader = !showingForwardLoader || timelineModels.isNotEmpty()
// We can hide the loader but still add the item to controller so it can trigger backwards pagination
Expand Down Expand Up @@ -327,6 +339,9 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
}

private fun buildCacheItemsIfNeeded() = synchronized(modelCache) {
hasUTD = false
hasReachedInvite = false

if (modelCache.isEmpty()) {
return
}
Expand All @@ -342,13 +357,21 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
private fun buildCacheItem(currentPosition: Int, items: List<TimelineEvent>): CacheItemData {
val event = items[currentPosition]
val nextEvent = items.nextOrNull(currentPosition)
val date = event.root.localDateTime()
val nextDate = nextEvent?.root?.localDateTime()
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
if (hasReachedInvite && hasUTD) {
return CacheItemData(event.localId, event.root.eventId, null, null, null)
}
updateUTDStates(event, nextEvent)
val eventModel = timelineItemFactory.create(event, nextEvent, eventIdToHighlight, callback).also {
it.id(event.localId)
it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event))
}
val addDaySeparator = if (hasReachedInvite && hasUTD) {
true
} else {
val date = event.root.localDateTime()
val nextDate = nextEvent?.root?.localDateTime()
date.toLocalDate() != nextDate?.toLocalDate()
}
val mergedHeaderModel = mergedHeaderItemFactory.create(event,
nextEvent = nextEvent,
items = items,
Expand All @@ -372,6 +395,27 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
}
}

private fun updateUTDStates(event: TimelineEvent, nextEvent: TimelineEvent?) {
if (vectorPreferences.labShowCompleteHistoryInEncryptedRoom()) {
return
}
if (event.root.type == EventType.STATE_ROOM_MEMBER
&& event.root.stateKey == session.myUserId) {
val content = event.root.content.toModel<RoomMemberContent>()
if (content?.membership == Membership.INVITE) {
hasReachedInvite = true
} else if (content?.membership == Membership.JOIN) {
val prevContent = event.root.resolvedPrevContent().toModel<RoomMemberContent>()
if (prevContent?.membership?.isActive() == false) {
hasReachedInvite = true
}
}
}
if (nextEvent?.root?.getClearType() == EventType.ENCRYPTED) {
hasUTD = true
}
}

/**
* Return true if added
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,19 @@ import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEve
import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEventsItem_
import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem
import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem_
import im.vector.app.features.home.room.detail.timeline.item.MergedUTDItem
import im.vector.app.features.home.room.detail.timeline.item.MergedUTDItem_
import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.extensions.orFalse
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.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
import timber.log.Timber
import javax.inject.Inject

class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
private val avatarRenderer: AvatarRenderer,
private val avatarSizeProvider: AvatarSizeProvider,
private val roomSummaryHolder: RoomSummaryHolder,
private val vectorPreferences: VectorPreferences) {
private val roomSummaryHolder: RoomSummaryHolder) {

private val collapsedEventIds = linkedSetOf<Long>()
private val mergeItemCollapseStates = HashMap<Long, Boolean>()
Expand All @@ -66,10 +61,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
callback: TimelineEventController.Callback?,
requestModelBuild: () -> Unit)
: BasedMergedItem<*>? {
return if (shouldMergedAsCannotDecryptGroup(event, nextEvent)) {
Timber.v("## MERGE: Candidate for merge, top event ${event.eventId}")
buildUTDMergedSummary(currentPosition, items, event, eventIdToHighlight, /*requestModelBuild,*/ callback)
} else if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE
return if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE
&& event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel<RoomCreateContent>()?.creator)) {
// It's the first item before room.create
// Collapse all room configuration events
Expand Down Expand Up @@ -144,83 +136,6 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
}
}

// Event should be UTD
// Next event should not
private fun shouldMergedAsCannotDecryptGroup(event: TimelineEvent, nextEvent: TimelineEvent?): Boolean {
if (!vectorPreferences.mergeUTDinTimeline()) return false
// if event is not UTD return false
if (!isEventUTD(event)) return false
// At this point event cannot be decrypted
// Let's check if older event is not UTD
return nextEvent == null || !isEventUTD(event)
}

private fun isEventUTD(event: TimelineEvent): Boolean {
return event.root.getClearType() == EventType.ENCRYPTED && !event.root.isRedacted()
}

private fun buildUTDMergedSummary(currentPosition: Int,
items: List<TimelineEvent>,
event: TimelineEvent,
eventIdToHighlight: String?,
// requestModelBuild: () -> Unit,
callback: TimelineEventController.Callback?): MergedUTDItem_? {
Timber.v("## MERGE: buildUTDMergedSummary from position $currentPosition")
var prevEvent = items.prevOrNull(currentPosition)
var tmpPos = currentPosition - 1
val mergedEvents = ArrayList<TimelineEvent>().also { it.add(event) }

while (prevEvent != null && isEventUTD(prevEvent)) {
mergedEvents.add(prevEvent)
tmpPos--
prevEvent = if (tmpPos >= 0) items[tmpPos] else null
}

Timber.v("## MERGE: buildUTDMergedSummary merge group size ${mergedEvents.size}")
if (mergedEvents.size < 3) return null

var highlighted = false
val mergedData = ArrayList<BasedMergedItem.Data>(mergedEvents.size)
mergedEvents.reversed()
.forEach { mergedEvent ->
if (!highlighted && mergedEvent.root.eventId == eventIdToHighlight) {
highlighted = true
}
val senderAvatar = mergedEvent.senderInfo.avatarUrl
val senderName = mergedEvent.senderInfo.disambiguatedDisplayName
val data = BasedMergedItem.Data(
userId = mergedEvent.root.senderId ?: "",
avatarUrl = senderAvatar,
memberName = senderName,
localId = mergedEvent.localId,
eventId = mergedEvent.root.eventId ?: "",
isDirectRoom = isDirectRoom()
)
mergedData.add(data)
}
val mergedEventIds = mergedEvents.map { it.localId }

collapsedEventIds.addAll(mergedEventIds)

val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() }

val attributes = MergedUTDItem.Attributes(
isCollapsed = true,
mergeData = mergedData,
avatarRenderer = avatarRenderer,
onCollapsedStateChanged = {}
)
return MergedUTDItem_()
.id(mergeId)
.big(mergedEventIds.size > 5)
.leftGuideline(avatarSizeProvider.leftGuideline)
.highlighted(highlighted)
.attributes(attributes)
.also {
it.setOnVisibilityStateChanged(MergedTimelineEventVisibilityStateChangedListener(callback, mergedEvents))
}
}

private fun buildRoomCreationMergedSummary(currentPosition: Int,
items: List<TimelineEvent>,
event: TimelineEvent,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
private const val SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY"

// SETTINGS_LABS_HIDE_TECHNICAL_E2E_ERRORS
private const val SETTINGS_LABS_MERGE_E2E_ERRORS = "SETTINGS_LABS_MERGE_E2E_ERRORS"
private const val SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM = "SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM"
const val SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB = "SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB"

// analytics
Expand Down Expand Up @@ -285,8 +285,8 @@ class VectorPreferences @Inject constructor(private val context: Context) {
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY, true)
}

fun mergeUTDinTimeline(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_LABS_MERGE_E2E_ERRORS, false)
fun labShowCompleteHistoryInEncryptedRoom(): Boolean {
return developerMode() && defaultPrefs.getBoolean(SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM, false)
}

fun labAllowedExtendedLogging(): Boolean {
Expand Down
1 change: 1 addition & 0 deletions vector/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1710,6 +1710,7 @@
<string name="send_suggestion_failed">The suggestion failed to be sent (%s)</string>

<string name="settings_labs_show_hidden_events_in_timeline">Show hidden events in timeline</string>
<string name="settings_labs_show_complete_history_in_encrypted_room">"Show complete history in encrypted rooms"</string>

<string name="bottom_action_people_x">Direct Messages</string>

Expand Down
Loading

0 comments on commit 2a21871

Please sign in to comment.