Pinned events : clean up banner code and add small test.

This commit is contained in:
ganfra 2024-07-31 13:44:46 +02:00
parent a8115e0a21
commit 88c07511d8
6 changed files with 69 additions and 27 deletions

View file

@ -41,7 +41,6 @@ import io.element.android.features.messages.impl.actionlist.model.TimelineItemAc
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerPresenter
import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState
import io.element.android.features.messages.impl.timeline.TimelineController import io.element.android.features.messages.impl.timeline.TimelineController
import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelineEvents

View file

@ -26,7 +26,6 @@ import io.element.android.libraries.architecture.Presenter
import javax.inject.Inject import javax.inject.Inject
class PinnedMessagesBannerPresenter @Inject constructor() : Presenter<PinnedMessagesBannerState> { class PinnedMessagesBannerPresenter @Inject constructor() : Presenter<PinnedMessagesBannerState> {
@Composable @Composable
override fun present(): PinnedMessagesBannerState { override fun present(): PinnedMessagesBannerState {
var pinnedMessageCount by remember { var pinnedMessageCount by remember {

View file

@ -19,7 +19,6 @@ package io.element.android.features.messages.impl.pinned.banner
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
internal class PinnedMessagesBannerStateProvider : PreviewParameterProvider<PinnedMessagesBannerState> { internal class PinnedMessagesBannerStateProvider : PreviewParameterProvider<PinnedMessagesBannerState> {
override val values: Sequence<PinnedMessagesBannerState> override val values: Sequence<PinnedMessagesBannerState>
get() = sequenceOf( get() = sequenceOf(
aPinnedMessagesBannerState(pinnedMessagesCount = 1, currentPinnedMessageIndex = 0), aPinnedMessagesBannerState(pinnedMessagesCount = 1, currentPinnedMessageIndex = 0),

View file

@ -32,15 +32,12 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawBehind
@ -138,7 +135,7 @@ private fun PinIndicators(
val viewportSize = lazyListState.layoutInfo.viewportSize val viewportSize = lazyListState.layoutInfo.viewportSize
lazyListState.animateScrollToItem( lazyListState.animateScrollToItem(
pinIndex, pinIndex,
(indicatorHeight / 2 - viewportSize.height / 2) indicatorHeight / 2 - viewportSize.height / 2
) )
} }
LazyColumn( LazyColumn(
@ -192,13 +189,12 @@ private fun PinnedMessageItem(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
maxLines = 1, maxLines = 1,
) )
} }
} }
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
fun PinnedMessagesBannerViewPreview(@PreviewParameter(PinnedMessagesBannerStateProvider::class) state: PinnedMessagesBannerState) = ElementPreview { internal fun PinnedMessagesBannerViewPreview(@PreviewParameter(PinnedMessagesBannerStateProvider::class) state: PinnedMessagesBannerState) = ElementPreview {
PinnedMessagesBannerView( PinnedMessagesBannerView(
state = state, state = state,
) )

View file

@ -58,7 +58,6 @@ import io.element.android.features.poll.test.actions.FakeEndPollAction
import io.element.android.features.poll.test.actions.FakeSendPollResponseAction import io.element.android.features.poll.test.actions.FakeSendPollResponseAction
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
@ -160,7 +159,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
assertThat(room.markAsReadCalls).isEmpty() assertThat(room.markAsReadCalls).isEmpty()
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -181,7 +180,7 @@ class MessagesPresenterTest {
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
).apply { ).apply {
givenRoomInfo(aRoomInfo(hasRoomCall = true)) givenRoomInfo(aRoomInfo(hasRoomCall = true))
} }
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
@ -210,7 +209,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -248,7 +247,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -307,7 +306,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter( val presenter = createMessagesPresenter(
clipboardHelper = clipboardHelper, clipboardHelper = clipboardHelper,
matrixRoom = matrixRoom, matrixRoom = matrixRoom,
@ -497,7 +496,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) } val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
liveTimeline.redactEventLambda = redactEventLambda liveTimeline.redactEventLambda = redactEventLambda
@ -572,7 +571,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -608,7 +607,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -633,7 +632,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -658,7 +657,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
persistentListOf( persistentListOf(
@ -694,7 +693,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Error( MatrixRoomMembersState.Error(
failure = Throwable(), failure = Throwable(),
@ -731,7 +730,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
room.givenRoomMembersState(MatrixRoomMembersState.Unknown) room.givenRoomMembersState(MatrixRoomMembersState.Unknown)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -758,7 +757,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
persistentListOf( persistentListOf(
@ -799,7 +798,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -824,7 +823,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -846,7 +845,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -867,7 +866,7 @@ class MessagesPresenterTest {
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
) )
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.impl.pinned.banner
import com.google.common.truth.Truth.assertThat
import io.element.android.tests.testutils.test
import kotlinx.coroutines.test.runTest
import org.junit.Test
class PinnedMessagesBannerPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createPinnedMessagesBannerPresenter()
presenter.test {
val initialState = awaitItem()
assertThat(initialState.pinnedMessagesCount).isEqualTo(0)
assertThat(initialState.currentPinnedMessageIndex).isEqualTo(0)
}
}
@Test
fun `present - move to next pinned message when there is no pinned events`() = runTest {
val presenter = createPinnedMessagesBannerPresenter()
presenter.test {
val initialState = awaitItem()
initialState.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
// Nothing is emitted
ensureAllEventsConsumed()
}
}
private fun createPinnedMessagesBannerPresenter(): PinnedMessagesBannerPresenter {
return PinnedMessagesBannerPresenter()
}
}