Pinned events : use correct ordering logic

This commit is contained in:
ganfra 2024-08-06 19:05:08 +02:00
parent b13e121dc6
commit a7bf0b4900
4 changed files with 28 additions and 32 deletions

View file

@ -53,10 +53,10 @@ class PinnedMessagesBannerPresenter @Inject constructor(
override fun present(): PinnedMessagesBannerState { override fun present(): PinnedMessagesBannerState {
val isFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents).collectAsState(initial = false) val isFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents).collectAsState(initial = false)
var hasTimelineFailedToLoad by rememberSaveable { mutableStateOf(false) } var hasTimelineFailedToLoad by rememberSaveable { mutableStateOf(false) }
var currentPinnedMessageIndex by rememberSaveable { mutableIntStateOf(0) } var currentPinnedMessageIndex by rememberSaveable { mutableIntStateOf(-1) }
val knownPinnedMessagesCount by remember { val knownPinnedMessagesCount by remember {
room.roomInfoFlow.map { roomInfo -> roomInfo.pinnedEventIds.size } room.roomInfoFlow.map { roomInfo -> roomInfo.pinnedEventIds.size }
}.collectAsState(initial = null) }.collectAsState(initial = 0)
var pinnedItems by remember { var pinnedItems by remember {
mutableStateOf<ImmutableList<PinnedMessagesBannerItem>>(persistentListOf()) mutableStateOf<ImmutableList<PinnedMessagesBannerItem>>(persistentListOf())
@ -66,8 +66,8 @@ class PinnedMessagesBannerPresenter @Inject constructor(
isFeatureEnabled = isFeatureEnabled, isFeatureEnabled = isFeatureEnabled,
onItemsChange = { newItems -> onItemsChange = { newItems ->
val pinnedMessageCount = newItems.size val pinnedMessageCount = newItems.size
if (currentPinnedMessageIndex >= pinnedMessageCount) { if (currentPinnedMessageIndex >= pinnedMessageCount || currentPinnedMessageIndex < 0) {
currentPinnedMessageIndex = 0 currentPinnedMessageIndex = pinnedMessageCount - 1
} }
pinnedItems = newItems pinnedItems = newItems
}, },
@ -102,7 +102,7 @@ class PinnedMessagesBannerPresenter @Inject constructor(
private fun pinnedMessagesBannerState( private fun pinnedMessagesBannerState(
isFeatureEnabled: Boolean, isFeatureEnabled: Boolean,
hasTimelineFailed: Boolean, hasTimelineFailed: Boolean,
realPinnedMessagesCount: Int?, realPinnedMessagesCount: Int,
pinnedItems: ImmutableList<PinnedMessagesBannerItem>, pinnedItems: ImmutableList<PinnedMessagesBannerItem>,
currentPinnedMessageIndex: Int, currentPinnedMessageIndex: Int,
eventSink: (PinnedMessagesBannerEvents) -> Unit eventSink: (PinnedMessagesBannerEvents) -> Unit
@ -111,7 +111,7 @@ class PinnedMessagesBannerPresenter @Inject constructor(
return when { return when {
!isFeatureEnabled -> PinnedMessagesBannerState.Hidden !isFeatureEnabled -> PinnedMessagesBannerState.Hidden
hasTimelineFailed -> PinnedMessagesBannerState.Hidden hasTimelineFailed -> PinnedMessagesBannerState.Hidden
realPinnedMessagesCount == null || realPinnedMessagesCount == 0 -> PinnedMessagesBannerState.Hidden realPinnedMessagesCount == 0 -> PinnedMessagesBannerState.Hidden
currentPinnedMessage == null -> PinnedMessagesBannerState.Loading(realPinnedMessagesCount = realPinnedMessagesCount) currentPinnedMessage == null -> PinnedMessagesBannerState.Loading(realPinnedMessagesCount = realPinnedMessagesCount)
else -> { else -> {
PinnedMessagesBannerState.Loaded( PinnedMessagesBannerState.Loaded(

View file

@ -26,13 +26,14 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Immutable @Immutable
sealed interface PinnedMessagesBannerState { sealed interface PinnedMessagesBannerState {
data object Hidden : PinnedMessagesBannerState data object Hidden : PinnedMessagesBannerState
data class Loading(val realPinnedMessagesCount: Int) : PinnedMessagesBannerState sealed interface Visible : PinnedMessagesBannerState
data class Loading(val realPinnedMessagesCount: Int) : Visible
data class Loaded( data class Loaded(
val currentPinnedMessage: PinnedMessagesBannerItem, val currentPinnedMessage: PinnedMessagesBannerItem,
val currentPinnedMessageIndex: Int, val currentPinnedMessageIndex: Int,
val knownPinnedMessagesCount: Int, val knownPinnedMessagesCount: Int,
val eventSink: (PinnedMessagesBannerEvents) -> Unit val eventSink: (PinnedMessagesBannerEvents) -> Unit
) : PinnedMessagesBannerState ) : Visible
fun pinnedMessagesCount() = when (this) { fun pinnedMessagesCount() = when (this) {
is Hidden -> 0 is Hidden -> 0
@ -42,7 +43,7 @@ sealed interface PinnedMessagesBannerState {
fun currentPinnedMessageIndex() = when (this) { fun currentPinnedMessageIndex() = when (this) {
is Hidden -> 0 is Hidden -> 0
is Loading -> 0 is Loading -> realPinnedMessagesCount - 1
is Loaded -> currentPinnedMessageIndex is Loaded -> currentPinnedMessageIndex
} }

View file

@ -74,22 +74,11 @@ fun PinnedMessagesBannerView(
Box(modifier = modifier) { Box(modifier = modifier) {
when (state) { when (state) {
PinnedMessagesBannerState.Hidden -> Unit PinnedMessagesBannerState.Hidden -> Unit
is PinnedMessagesBannerState.Loading -> { is PinnedMessagesBannerState.Visible -> {
PinnedMessagesBannerRow( PinnedMessagesBannerRow(
state = state, state = state,
onClick = onClick,
onViewAllClick = onViewAllClick, onViewAllClick = onViewAllClick,
modifier = Modifier.clickable(onClick = { }),
)
}
is PinnedMessagesBannerState.Loaded -> {
PinnedMessagesBannerRow(
state = state,
onViewAllClick = onViewAllClick,
modifier = Modifier.clickable(
onClick = {
onClick(state.currentPinnedMessage.eventId)
state.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
}),
) )
} }
} }
@ -99,6 +88,7 @@ fun PinnedMessagesBannerView(
@Composable @Composable
fun PinnedMessagesBannerRow( fun PinnedMessagesBannerRow(
state: PinnedMessagesBannerState, state: PinnedMessagesBannerState,
onClick: (EventId) -> Unit,
onViewAllClick: () -> Unit, onViewAllClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@ -108,7 +98,13 @@ fun PinnedMessagesBannerRow(
.background(color = ElementTheme.colors.bgCanvasDefault) .background(color = ElementTheme.colors.bgCanvasDefault)
.fillMaxWidth() .fillMaxWidth()
.drawBorder(borderColor) .drawBorder(borderColor)
.heightIn(min = 64.dp), .heightIn(min = 64.dp)
.clickable {
if (state is PinnedMessagesBannerState.Loaded) {
onClick(state.currentPinnedMessage.eventId)
state.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
}
},
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = spacedBy(10.dp) horizontalArrangement = spacedBy(10.dp)
) { ) {
@ -202,7 +198,6 @@ private fun PinIndicators(
state = lazyListState, state = lazyListState,
verticalArrangement = spacedBy(2.dp), verticalArrangement = spacedBy(2.dp),
userScrollEnabled = false, userScrollEnabled = false,
reverseLayout = true
) { ) {
items(pinsCount) { index -> items(pinsCount) { index ->
Box( Box(

View file

@ -138,14 +138,6 @@ class PinnedMessagesBannerPresenterTest {
val presenter = createPinnedMessagesBannerPresenter(room = room) val presenter = createPinnedMessagesBannerPresenter(room = room)
presenter.test { presenter.test {
skipItems(2) skipItems(2)
awaitItem().also { loadedState ->
loadedState as PinnedMessagesBannerState.Loaded
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(0)
assertThat(loadedState.knownPinnedMessagesCount).isEqualTo(2)
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent1.toString())
loadedState.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
}
awaitItem().also { loadedState -> awaitItem().also { loadedState ->
loadedState as PinnedMessagesBannerState.Loaded loadedState as PinnedMessagesBannerState.Loaded
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(1) assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(1)
@ -159,6 +151,14 @@ class PinnedMessagesBannerPresenterTest {
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(0) assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(0)
assertThat(loadedState.knownPinnedMessagesCount).isEqualTo(2) assertThat(loadedState.knownPinnedMessagesCount).isEqualTo(2)
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent1.toString()) assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent1.toString())
loadedState.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
}
awaitItem().also { loadedState ->
loadedState as PinnedMessagesBannerState.Loaded
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(1)
assertThat(loadedState.knownPinnedMessagesCount).isEqualTo(2)
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent2.toString())
} }
} }
} }