Pinned messages list : improve and fix code after PR review.
This commit is contained in:
parent
ec4aab0c12
commit
a37a6d249f
11 changed files with 80 additions and 29 deletions
|
|
@ -36,7 +36,6 @@ import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.replace
|
import com.bumble.appyx.navmodel.backstack.operation.replace
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.singleTop
|
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
|
|
@ -86,6 +85,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
class LoggedInFlowNode @AssistedInject constructor(
|
class LoggedInFlowNode @AssistedInject constructor(
|
||||||
|
|
@ -204,7 +204,8 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
val serverNames: List<String> = emptyList(),
|
val serverNames: List<String> = emptyList(),
|
||||||
val trigger: JoinedRoom.Trigger? = null,
|
val trigger: JoinedRoom.Trigger? = null,
|
||||||
val roomDescription: RoomDescription? = null,
|
val roomDescription: RoomDescription? = null,
|
||||||
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages()
|
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(),
|
||||||
|
val targetId: UUID = UUID.randomUUID(),
|
||||||
) : NavTarget
|
) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|
@ -311,7 +312,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
if (pushToBackstack) {
|
if (pushToBackstack) {
|
||||||
backstack.push(target)
|
backstack.push(target)
|
||||||
} else {
|
} else {
|
||||||
backstack.singleTop(target)
|
backstack.replace(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PermalinkData.FallbackLink,
|
is PermalinkData.FallbackLink,
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ interface MessagesEntryPoint : FeatureEntryPoint {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onRoomDetailsClick()
|
fun onRoomDetailsClick()
|
||||||
fun onUserDataClick(userId: UserId)
|
fun onUserDataClick(userId: UserId)
|
||||||
fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean = true)
|
fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
|
||||||
fun onForwardedToSingleRoom(roomId: RoomId)
|
fun onForwardedToSingleRoom(roomId: RoomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ class MessagesFlowNode @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPermalinkClick(data: PermalinkData) {
|
override fun onPermalinkClick(data: PermalinkData) {
|
||||||
callbacks.forEach { it.onPermalinkClick(data) }
|
callbacks.forEach { it.onPermalinkClick(data, pushToBackstack = true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
|
||||||
fun create(navigator: PinnedMessagesListNavigator): PinnedMessagesListPresenter
|
fun create(navigator: PinnedMessagesListNavigator): PinnedMessagesListPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
private val actionListPresenter = actionListPresenterFactory.create(PinnedMessagesListTimelineActionPostProcessor)
|
private val actionListPresenter = actionListPresenterFactory.create(PinnedMessagesListTimelineActionPostProcessor())
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): PinnedMessagesListState {
|
override fun present(): PinnedMessagesListState {
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,17 @@ import io.element.android.features.messages.impl.UserEventPermissions
|
||||||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||||
import io.element.android.features.messages.impl.actionlist.anActionListState
|
import io.element.android.features.messages.impl.actionlist.anActionListState
|
||||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemList
|
import io.element.android.features.messages.impl.timeline.aTimelineItemDaySeparator
|
||||||
|
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||||
|
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
|
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemAudioContent
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemFileContent
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
open class PinnedMessagesListStateProvider : PreviewParameterProvider<PinnedMessagesListState> {
|
open class PinnedMessagesListStateProvider : PreviewParameterProvider<PinnedMessagesListState> {
|
||||||
|
|
@ -34,7 +41,45 @@ open class PinnedMessagesListStateProvider : PreviewParameterProvider<PinnedMess
|
||||||
aLoadingPinnedMessagesListState(),
|
aLoadingPinnedMessagesListState(),
|
||||||
anEmptyPinnedMessagesListState(),
|
anEmptyPinnedMessagesListState(),
|
||||||
aLoadedPinnedMessagesListState(
|
aLoadedPinnedMessagesListState(
|
||||||
timelineItems = aTimelineItemList(aTimelineItemTextContent())
|
timelineItems = persistentListOf(
|
||||||
|
aTimelineItemEvent(
|
||||||
|
isMine = false,
|
||||||
|
content = aTimelineItemTextContent("A pinned message"),
|
||||||
|
groupPosition = TimelineItemGroupPosition.Last,
|
||||||
|
timelineItemReactions = aTimelineItemReactions(0)
|
||||||
|
),
|
||||||
|
aTimelineItemEvent(
|
||||||
|
isMine = false,
|
||||||
|
content = aTimelineItemAudioContent("A pinned file"),
|
||||||
|
groupPosition = TimelineItemGroupPosition.Middle,
|
||||||
|
timelineItemReactions = aTimelineItemReactions(0)
|
||||||
|
),
|
||||||
|
aTimelineItemEvent(
|
||||||
|
isMine = false,
|
||||||
|
content = aTimelineItemPollContent("A pinned poll?"),
|
||||||
|
groupPosition = TimelineItemGroupPosition.First,
|
||||||
|
timelineItemReactions = aTimelineItemReactions(0)
|
||||||
|
),
|
||||||
|
aTimelineItemDaySeparator(),
|
||||||
|
aTimelineItemEvent(
|
||||||
|
isMine = true,
|
||||||
|
content = aTimelineItemTextContent("A pinned message"),
|
||||||
|
groupPosition = TimelineItemGroupPosition.Last,
|
||||||
|
timelineItemReactions = aTimelineItemReactions(0)
|
||||||
|
),
|
||||||
|
aTimelineItemEvent(
|
||||||
|
isMine = true,
|
||||||
|
content = aTimelineItemFileContent("A pinned file?"),
|
||||||
|
groupPosition = TimelineItemGroupPosition.Middle,
|
||||||
|
timelineItemReactions = aTimelineItemReactions(0)
|
||||||
|
),
|
||||||
|
aTimelineItemEvent(
|
||||||
|
isMine = true,
|
||||||
|
content = aTimelineItemPollContent("A pinned poll?"),
|
||||||
|
groupPosition = TimelineItemGroupPosition.First,
|
||||||
|
timelineItemReactions = aTimelineItemReactions(0)
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,21 +19,14 @@ package io.element.android.features.messages.impl.pinned.list
|
||||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionPostProcessor
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionPostProcessor
|
||||||
|
|
||||||
object PinnedMessagesListTimelineActionPostProcessor : TimelineItemActionPostProcessor {
|
class PinnedMessagesListTimelineActionPostProcessor : TimelineItemActionPostProcessor {
|
||||||
override fun process(actions: List<TimelineItemAction>): List<TimelineItemAction> {
|
override fun process(actions: List<TimelineItemAction>): List<TimelineItemAction> {
|
||||||
return buildList {
|
return buildList {
|
||||||
add(TimelineItemAction.ViewInTimeline)
|
add(TimelineItemAction.ViewInTimeline)
|
||||||
addAll(actions.filter(::predicate))
|
actions.firstOrNull { it is TimelineItemAction.Unpin }?.let(::add)
|
||||||
}
|
actions.firstOrNull { it is TimelineItemAction.Forward }?.let(::add)
|
||||||
}
|
actions.firstOrNull { it is TimelineItemAction.ViewSource }?.let(::add)
|
||||||
|
actions.firstOrNull { it is TimelineItemAction.Redact }?.let(::add)
|
||||||
private fun predicate(action: TimelineItemAction): Boolean {
|
|
||||||
return when (action) {
|
|
||||||
is TimelineItemAction.Unpin,
|
|
||||||
is TimelineItemAction.Redact,
|
|
||||||
is TimelineItemAction.Forward,
|
|
||||||
is TimelineItemAction.ViewSource -> true
|
|
||||||
else -> false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_THROWABLE
|
||||||
import io.element.android.libraries.matrix.test.A_UNIQUE_ID
|
import io.element.android.libraries.matrix.test.A_UNIQUE_ID
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
|
|
@ -172,10 +173,10 @@ class PinnedMessagesListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - unpin event`() = runTest {
|
fun `present - unpin event`() = runTest {
|
||||||
val unpinEventLambda = lambdaRecorder { _: EventId? -> Result.success(true) }
|
val successUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.success(true) }
|
||||||
val pinnedEventsTimeline = createPinnedMessagesTimeline().apply {
|
val failureUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.failure<Boolean>(A_THROWABLE) }
|
||||||
this.unpinEventLambda = unpinEventLambda
|
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
||||||
}
|
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
pinnedEventsTimelineResult = { Result.success(pinnedEventsTimeline) },
|
pinnedEventsTimelineResult = { Result.success(pinnedEventsTimeline) },
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
|
@ -189,9 +190,20 @@ class PinnedMessagesListPresenterTest {
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||||
|
|
||||||
|
pinnedEventsTimeline.unpinEventLambda = successUnpinEventLambda
|
||||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Unpin, eventItem))
|
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Unpin, eventItem))
|
||||||
|
|
||||||
|
pinnedEventsTimeline.unpinEventLambda = failureUnpinEventLambda
|
||||||
|
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Unpin, eventItem))
|
||||||
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
assert(unpinEventLambda)
|
|
||||||
|
assert(successUnpinEventLambda)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(AN_EVENT_ID))
|
||||||
|
|
||||||
|
assert(failureUnpinEventLambda)
|
||||||
.isCalledOnce()
|
.isCalledOnce()
|
||||||
.with(value(AN_EVENT_ID))
|
.with(value(AN_EVENT_ID))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ interface Timeline : AutoCloseable {
|
||||||
enum class Mode {
|
enum class Mode {
|
||||||
LIVE,
|
LIVE,
|
||||||
FOCUSED_ON_EVENT,
|
FOCUSED_ON_EVENT,
|
||||||
FOCUSED_ON_PINNED_EVENTS
|
PINNED_EVENTS
|
||||||
}
|
}
|
||||||
|
|
||||||
val membershipChangeEventReceived: Flow<Unit>
|
val membershipChangeEventReceived: Flow<Unit>
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ class RustMatrixRoom(
|
||||||
internalIdPrefix = "pinned_events",
|
internalIdPrefix = "pinned_events",
|
||||||
maxEventsToLoad = 100u,
|
maxEventsToLoad = 100u,
|
||||||
).let { inner ->
|
).let { inner ->
|
||||||
createTimeline(inner, mode = Timeline.Mode.FOCUSED_ON_PINNED_EVENTS)
|
createTimeline(inner, mode = Timeline.Mode.PINNED_EVENTS)
|
||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
if (it is CancellationException) {
|
if (it is CancellationException) {
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ class RustTimeline(
|
||||||
private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode)
|
private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode)
|
||||||
|
|
||||||
private val backPaginationStatus = MutableStateFlow(
|
private val backPaginationStatus = MutableStateFlow(
|
||||||
Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = mode != Timeline.Mode.FOCUSED_ON_PINNED_EVENTS)
|
Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = mode != Timeline.Mode.PINNED_EVENTS)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val forwardPaginationStatus = MutableStateFlow(
|
private val forwardPaginationStatus = MutableStateFlow(
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) {
|
||||||
hasMoreToLoadBackwards: Boolean
|
hasMoreToLoadBackwards: Boolean
|
||||||
): List<MatrixTimelineItem> {
|
): List<MatrixTimelineItem> {
|
||||||
return when {
|
return when {
|
||||||
mode == Timeline.Mode.FOCUSED_ON_PINNED_EVENTS -> items
|
mode == Timeline.Mode.PINNED_EVENTS -> items
|
||||||
hasMoreToLoadBackwards -> items
|
hasMoreToLoadBackwards -> items
|
||||||
isDm -> processForDM(items)
|
isDm -> processForDM(items)
|
||||||
else -> processForRoom(items)
|
else -> processForRoom(items)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue