Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/utd pagination #2190

Merged
merged 3 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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