Merge branch 'develop' into fix/start-voice-recording-when-permission-is-granted
This commit is contained in:
commit
307e6a7cd2
215 changed files with 2436 additions and 2080 deletions
|
|
@ -210,10 +210,7 @@ class MessagesPresenter(
|
|||
// * History sharing is enabled,
|
||||
// * The room is encrypted, and:
|
||||
// * The room's history_visibility allows future users to see content.
|
||||
val showSharedHistoryIcon = isKeyShareOnInviteEnabled &&
|
||||
roomInfo.isEncrypted == true &&
|
||||
(roomInfo.historyVisibility == RoomHistoryVisibility.Shared ||
|
||||
roomInfo.historyVisibility == RoomHistoryVisibility.WorldReadable)
|
||||
val topBarSharedHistoryIcon = if (isKeyShareOnInviteEnabled) roomInfo.sharedHistoryIcon() else SharedHistoryIcon.NONE
|
||||
|
||||
LifecycleResumeEffect(dmRoomMember, roomInfo.isEncrypted) {
|
||||
if (roomInfo.isEncrypted == true) {
|
||||
|
|
@ -297,12 +294,24 @@ class MessagesPresenter(
|
|||
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
||||
dmUserVerificationState = dmUserVerificationState,
|
||||
roomMemberModerationState = roomMemberModerationState,
|
||||
showSharedHistoryIcon = showSharedHistoryIcon,
|
||||
topBarSharedHistoryIcon = topBarSharedHistoryIcon,
|
||||
successorRoom = roomInfo.successorRoom,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun RoomInfo.sharedHistoryIcon(): SharedHistoryIcon {
|
||||
if (isEncrypted == true) {
|
||||
if (historyVisibility == RoomHistoryVisibility.Shared) {
|
||||
return SharedHistoryIcon.SHARED
|
||||
} else if (historyVisibility == RoomHistoryVisibility.WorldReadable) {
|
||||
return SharedHistoryIcon.WORLD_READABLE
|
||||
}
|
||||
}
|
||||
|
||||
return SharedHistoryIcon.NONE
|
||||
}
|
||||
|
||||
private fun RoomInfo.avatarData(): AvatarData {
|
||||
return AvatarData(
|
||||
id = id.value,
|
||||
|
|
|
|||
|
|
@ -54,10 +54,22 @@ data class MessagesState(
|
|||
val pinnedMessagesBannerState: PinnedMessagesBannerState,
|
||||
val dmUserVerificationState: IdentityState?,
|
||||
val roomMemberModerationState: RoomMemberModerationState,
|
||||
/** Should the top bar include the "history" icon? */
|
||||
val showSharedHistoryIcon: Boolean,
|
||||
/** Type of "shared history" icon to show in the top bar. */
|
||||
val topBarSharedHistoryIcon: SharedHistoryIcon,
|
||||
val successorRoom: SuccessorRoom?,
|
||||
val eventSink: (MessagesEvent) -> Unit
|
||||
) {
|
||||
val isTombstoned = successorRoom != null
|
||||
}
|
||||
|
||||
/** Type of "shared history" icon to show in the top bar. */
|
||||
enum class SharedHistoryIcon {
|
||||
/** Show no icon at all. */
|
||||
NONE,
|
||||
|
||||
/** history_visibility: shared. */
|
||||
SHARED,
|
||||
|
||||
/** history_visibility: world_readable. */
|
||||
WORLD_READABLE
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ fun aMessagesState(
|
|||
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
|
||||
dmUserVerificationState: IdentityState? = null,
|
||||
roomMemberModerationState: RoomMemberModerationState = aRoomMemberModerationState(),
|
||||
showSharedHistoryIcon: Boolean = false,
|
||||
topBarSharedHistoryIcon: SharedHistoryIcon = SharedHistoryIcon.NONE,
|
||||
successorRoom: SuccessorRoom? = null,
|
||||
eventSink: (MessagesEvent) -> Unit = {},
|
||||
) = MessagesState(
|
||||
|
|
@ -148,7 +148,7 @@ fun aMessagesState(
|
|||
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
||||
dmUserVerificationState = dmUserVerificationState,
|
||||
roomMemberModerationState = roomMemberModerationState,
|
||||
showSharedHistoryIcon = showSharedHistoryIcon,
|
||||
topBarSharedHistoryIcon = topBarSharedHistoryIcon,
|
||||
successorRoom = successorRoom,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ fun MessagesView(
|
|||
heroes = state.heroes,
|
||||
roomCallState = state.roomCallState,
|
||||
dmUserIdentityState = state.dmUserVerificationState,
|
||||
showSharedHistoryIcon = state.showSharedHistoryIcon,
|
||||
sharedHistoryIcon = state.topBarSharedHistoryIcon,
|
||||
onBackClick = { hidingKeyboard { onBackClick() } },
|
||||
onRoomDetailsClick = { hidingKeyboard { onRoomDetailsClick() } },
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bumble.appyx.core.lifecycle.subscribe
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
|
|
@ -29,6 +30,7 @@ import io.element.android.annotations.ContributesNode
|
|||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.messages.impl.MessagesNavigator
|
||||
import io.element.android.features.messages.impl.MessagesPresenter
|
||||
import io.element.android.features.messages.impl.MessagesState
|
||||
import io.element.android.features.messages.impl.MessagesView
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListPresenter
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionPostProcessor
|
||||
|
|
@ -44,11 +46,11 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
|||
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
||||
import io.element.android.libraries.androidutils.system.openUrlInExternalApp
|
||||
import io.element.android.libraries.architecture.NodeInputs
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.analytics.toAnalyticsViewRoom
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -67,22 +69,19 @@ import io.element.android.services.analytics.api.AnalyticsService
|
|||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@ContributesNode(RoomScope::class)
|
||||
@AssistedInject
|
||||
class ThreadedMessagesNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
|
||||
private val room: JoinedRoom,
|
||||
private val analyticsService: AnalyticsService,
|
||||
messageComposerPresenterFactory: MessageComposerPresenter.Factory,
|
||||
timelinePresenterFactory: TimelinePresenter.Factory,
|
||||
presenterFactory: MessagesPresenter.Factory,
|
||||
actionListPresenterFactory: ActionListPresenter.Factory,
|
||||
private val messageComposerPresenterFactory: MessageComposerPresenter.Factory,
|
||||
private val timelinePresenterFactory: TimelinePresenter.Factory,
|
||||
private val presenterFactory: MessagesPresenter.Factory,
|
||||
private val actionListPresenterFactory: ActionListPresenter.Factory,
|
||||
private val timelineItemPresenterFactories: TimelineItemPresenterFactories,
|
||||
private val mediaPlayer: MediaPlayer,
|
||||
private val permalinkParser: PermalinkParser,
|
||||
|
|
@ -96,20 +95,29 @@ class ThreadedMessagesNode(
|
|||
private val inputs = inputs<Inputs>()
|
||||
private val callback: Callback = callback()
|
||||
|
||||
// TODO use a loading state node to preload this instead of using `runBlocking`
|
||||
private val threadedTimeline = runBlocking { room.createTimeline(CreateTimelineParams.Threaded(threadRootEventId = inputs.threadRootEventId)).getOrThrow() }
|
||||
private val timelineController = TimelineController(room, threadedTimeline)
|
||||
private val presenter = presenterFactory.create(
|
||||
navigator = this,
|
||||
composerPresenter = messageComposerPresenterFactory.create(timelineController, this),
|
||||
timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this),
|
||||
// TODO add special processor for threaded timeline
|
||||
actionListPresenter = actionListPresenterFactory.create(
|
||||
postProcessor = TimelineItemActionPostProcessor.Default,
|
||||
timelineMode = timelineController.mainTimelineMode(),
|
||||
),
|
||||
timelineController = timelineController,
|
||||
)
|
||||
private var timelineController: TimelineController? by mutableStateOf(null)
|
||||
private var presenter: Presenter<MessagesState>? by mutableStateOf(null)
|
||||
|
||||
/**
|
||||
* This should be fast to load, but not faster than several UI frames, which will cause ANRs.
|
||||
* We'll load the [presenter] in an async way to prevent this.
|
||||
*/
|
||||
private suspend fun createPresenter(): Presenter<MessagesState> {
|
||||
val threadedTimeline = room.createTimeline(CreateTimelineParams.Threaded(threadRootEventId = inputs.threadRootEventId)).getOrThrow()
|
||||
val timelineController = TimelineController(room, threadedTimeline)
|
||||
this.timelineController = timelineController
|
||||
return presenterFactory.create(
|
||||
navigator = this,
|
||||
composerPresenter = messageComposerPresenterFactory.create(timelineController, this),
|
||||
timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this),
|
||||
// TODO add special processor for threaded timeline
|
||||
actionListPresenter = actionListPresenterFactory.create(
|
||||
postProcessor = TimelineItemActionPostProcessor.Default,
|
||||
timelineMode = timelineController.mainTimelineMode(),
|
||||
),
|
||||
timelineController = timelineController,
|
||||
)
|
||||
}
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean
|
||||
|
|
@ -130,7 +138,10 @@ class ThreadedMessagesNode(
|
|||
super.onBuilt()
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
sessionCoroutineScope.launch { analyticsService.capture(room.toAnalyticsViewRoom()) }
|
||||
analyticsService.capture(room.toAnalyticsViewRoom())
|
||||
lifecycleScope.launch {
|
||||
presenter = createPresenter()
|
||||
}
|
||||
},
|
||||
onStart = {
|
||||
appNavigationStateService.onNavigateToThread(id, inputs.threadRootEventId)
|
||||
|
|
@ -231,56 +242,61 @@ class ThreadedMessagesNode(
|
|||
CompositionLocalProvider(
|
||||
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,
|
||||
) {
|
||||
val state = presenter.present()
|
||||
OnLifecycleEvent { _, event ->
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_PAUSE -> state.composerState.eventSink(MessageComposerEvent.SaveDraft)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
MessagesView(
|
||||
state = state,
|
||||
onBackClick = this::navigateUp,
|
||||
onRoomDetailsClick = {},
|
||||
onEventContentClick = { isLive, event ->
|
||||
if (isLive) {
|
||||
callback.handleEventClick(timelineController.mainTimelineMode(), event)
|
||||
} else {
|
||||
val detachedTimelineMode = timelineController.detachedTimelineMode()
|
||||
if (detachedTimelineMode != null) {
|
||||
callback.handleEventClick(detachedTimelineMode, event)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
// Only display the actual UI and lifecycle logic if the presenter is loaded
|
||||
presenter?.present()?.let { state ->
|
||||
OnLifecycleEvent { _, event ->
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_PAUSE -> state.composerState.eventSink(MessageComposerEvent.SaveDraft)
|
||||
else -> Unit
|
||||
}
|
||||
},
|
||||
onUserDataClick = callback::navigateToRoomMemberDetails,
|
||||
onLinkClick = { url, customTab ->
|
||||
onLinkClick(
|
||||
activity = activity,
|
||||
darkTheme = isDark,
|
||||
url = url,
|
||||
eventSink = state.timelineState.eventSink,
|
||||
customTab = customTab,
|
||||
)
|
||||
},
|
||||
onSendLocationClick = callback::navigateToSendLocation,
|
||||
onCreatePollClick = callback::navigateToCreatePoll,
|
||||
onJoinCallClick = { callback.navigateToRoomCall(room.roomId) },
|
||||
onViewAllPinnedMessagesClick = {},
|
||||
modifier = modifier,
|
||||
knockRequestsBannerView = {},
|
||||
)
|
||||
|
||||
var focusedEventId by rememberSaveable {
|
||||
mutableStateOf(inputs.focusedEventId)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
focusedEventId?.also { eventId ->
|
||||
state.timelineState.eventSink(TimelineEvent.FocusOnEvent(eventId))
|
||||
}
|
||||
// Reset the focused event id to null to avoid refocusing when restoring node.
|
||||
focusedEventId = null
|
||||
|
||||
MessagesView(
|
||||
state = state,
|
||||
onBackClick = this::navigateUp,
|
||||
onRoomDetailsClick = {},
|
||||
onEventContentClick = { isLive, event ->
|
||||
timelineController?.let { controller ->
|
||||
if (isLive) {
|
||||
callback.handleEventClick(controller.mainTimelineMode(), event)
|
||||
} else {
|
||||
val detachedTimelineMode = controller.detachedTimelineMode()
|
||||
if (detachedTimelineMode != null) {
|
||||
callback.handleEventClick(detachedTimelineMode, event)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
} == true
|
||||
},
|
||||
onUserDataClick = callback::navigateToRoomMemberDetails,
|
||||
onLinkClick = { url, customTab ->
|
||||
onLinkClick(
|
||||
activity = activity,
|
||||
darkTheme = isDark,
|
||||
url = url,
|
||||
eventSink = state.timelineState.eventSink,
|
||||
customTab = customTab,
|
||||
)
|
||||
},
|
||||
onSendLocationClick = callback::navigateToSendLocation,
|
||||
onCreatePollClick = callback::navigateToCreatePoll,
|
||||
onJoinCallClick = { callback.navigateToRoomCall(room.roomId) },
|
||||
onViewAllPinnedMessagesClick = {},
|
||||
modifier = modifier,
|
||||
knockRequestsBannerView = {},
|
||||
)
|
||||
|
||||
var focusedEventId by rememberSaveable {
|
||||
mutableStateOf(inputs.focusedEventId)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
focusedEventId?.also { eventId ->
|
||||
state.timelineState.eventSink(TimelineEvent.FocusOnEvent(eventId))
|
||||
}
|
||||
// Reset the focused event id to null to avoid refocusing when restoring node.
|
||||
focusedEventId = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.messages.impl.SharedHistoryIcon
|
||||
import io.element.android.features.messages.impl.timeline.components.CallMenuItem
|
||||
import io.element.android.features.roomcall.api.RoomCallState
|
||||
import io.element.android.features.roomcall.api.aStandByCallState
|
||||
|
|
@ -63,7 +64,7 @@ internal fun MessagesViewTopBar(
|
|||
heroes: ImmutableList<AvatarData>,
|
||||
roomCallState: RoomCallState,
|
||||
dmUserIdentityState: IdentityState?,
|
||||
showSharedHistoryIcon: Boolean,
|
||||
sharedHistoryIcon: SharedHistoryIcon,
|
||||
onRoomDetailsClick: () -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
|
|
@ -110,12 +111,18 @@ internal fun MessagesViewTopBar(
|
|||
else -> Unit
|
||||
}
|
||||
|
||||
if (showSharedHistoryIcon) {
|
||||
Icon(
|
||||
when (sharedHistoryIcon) {
|
||||
SharedHistoryIcon.NONE -> Unit
|
||||
SharedHistoryIcon.SHARED -> Icon(
|
||||
imageVector = CompoundIcons.History(),
|
||||
tint = ElementTheme.colors.iconInfoPrimary,
|
||||
contentDescription = stringResource(CommonStrings.common_shared_history),
|
||||
)
|
||||
SharedHistoryIcon.WORLD_READABLE -> Icon(
|
||||
imageVector = CompoundIcons.UserProfileSolid(),
|
||||
tint = ElementTheme.colors.iconInfoPrimary,
|
||||
contentDescription = stringResource(CommonStrings.common_world_readable_history),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -178,7 +185,7 @@ internal fun MessagesViewTopBarPreview() = ElementPreview {
|
|||
heroes: ImmutableList<AvatarData> = persistentListOf(),
|
||||
roomCallState: RoomCallState = RoomCallState.Unavailable,
|
||||
dmUserIdentityState: IdentityState? = null,
|
||||
showSharedHistoryIcon: Boolean = false,
|
||||
sharedHistoryIcon: SharedHistoryIcon = SharedHistoryIcon.NONE,
|
||||
) = MessagesViewTopBar(
|
||||
roomName = roomName,
|
||||
roomAvatar = roomAvatar,
|
||||
|
|
@ -186,7 +193,7 @@ internal fun MessagesViewTopBarPreview() = ElementPreview {
|
|||
heroes = heroes,
|
||||
roomCallState = roomCallState,
|
||||
dmUserIdentityState = dmUserIdentityState,
|
||||
showSharedHistoryIcon = showSharedHistoryIcon,
|
||||
sharedHistoryIcon = sharedHistoryIcon,
|
||||
onRoomDetailsClick = {},
|
||||
onJoinCallClick = {},
|
||||
onBackClick = {},
|
||||
|
|
@ -223,7 +230,12 @@ internal fun MessagesViewTopBarPreview() = ElementPreview {
|
|||
AMessagesViewTopBar(
|
||||
roomName = "A DM with shared history",
|
||||
dmUserIdentityState = IdentityState.Verified,
|
||||
showSharedHistoryIcon = true,
|
||||
sharedHistoryIcon = SharedHistoryIcon.SHARED,
|
||||
)
|
||||
HorizontalDivider()
|
||||
AMessagesViewTopBar(
|
||||
roomName = "A room with world_readable history",
|
||||
sharedHistoryIcon = SharedHistoryIcon.WORLD_READABLE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
<string name="screen_report_content_explanation">"Teade selle sõnumi kohta edastatakse sinu koduserveri haldajale. Haldajal ei ole võimalik lugeda krüptitud sõnumite sisu."</string>
|
||||
<string name="screen_report_content_hint">"Sellest sisust teatamise põhjus"</string>
|
||||
<string name="screen_room_attachment_source_camera">"Kaamera"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Tee pilt"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Pildista"</string>
|
||||
<string name="screen_room_attachment_source_camera_video">"Salvesta video"</string>
|
||||
<string name="screen_room_attachment_source_files">"Manus"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Fotode ja videote galerii"</string>
|
||||
|
|
|
|||
|
|
@ -1217,7 +1217,7 @@ class MessagesPresenterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `present - shows a "history" icon if the room is encrypted and history is shared`() = runTest {
|
||||
fun `present - shows a history icon if the room is encrypted and history is shared`() = runTest {
|
||||
val presenter = createMessagesPresenter(
|
||||
joinedRoom = FakeJoinedRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
|
|
@ -1233,7 +1233,28 @@ class MessagesPresenterTest {
|
|||
awaitItem()
|
||||
runCurrent()
|
||||
val state = awaitItem()
|
||||
assertThat(state.showSharedHistoryIcon).isTrue()
|
||||
assertThat(state.topBarSharedHistoryIcon).isEqualTo(SharedHistoryIcon.SHARED)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - shows a "world_readable" icon if the room is encrypted and history is world_readable`() = runTest {
|
||||
val presenter = createMessagesPresenter(
|
||||
joinedRoom = FakeJoinedRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
roomPermissions = roomPermissions(),
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = true, historyVisibility = RoomHistoryVisibility.WorldReadable),
|
||||
),
|
||||
),
|
||||
featureFlagService = FakeFeatureFlagService(
|
||||
initialState = mapOf(FeatureFlags.EnableKeyShareOnInvite.key to true)
|
||||
)
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
awaitItem()
|
||||
runCurrent()
|
||||
val state = awaitItem()
|
||||
assertThat(state.topBarSharedHistoryIcon).isEqualTo(SharedHistoryIcon.WORLD_READABLE)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import io.element.android.features.messages.impl.messagecomposer.suggestions.Roo
|
|||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeDynamicRoomList
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
|
@ -22,7 +23,10 @@ import org.junit.Test
|
|||
class DefaultRoomAliasSuggestionsDataSourceTest {
|
||||
@Test
|
||||
fun `getAllRoomAliasSuggestions must emit a list of room alias suggestions`() = runTest {
|
||||
val roomListService = FakeRoomListService()
|
||||
val roomList = FakeDynamicRoomList()
|
||||
val roomListService = FakeRoomListService(
|
||||
createRoomListLambda = { roomList }
|
||||
)
|
||||
val sut = DefaultRoomAliasSuggestionsDataSource(
|
||||
roomListService
|
||||
)
|
||||
|
|
@ -31,7 +35,7 @@ class DefaultRoomAliasSuggestionsDataSourceTest {
|
|||
)
|
||||
sut.getAllRoomAliasSuggestions().test {
|
||||
assertThat(awaitItem()).isEmpty()
|
||||
roomListService.postAllRooms(
|
||||
roomList.summaries.emit(
|
||||
listOf(
|
||||
aRoomSummary(roomId = A_ROOM_ID_2, canonicalAlias = null),
|
||||
aRoomSummaryWithAnAlias,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue