Merge pull request #6561 from element-hq/feature/bma/removeSpaceAnnouncement

Remove space announcement
This commit is contained in:
Benoit Marty 2026-04-14 16:58:25 +02:00 committed by GitHub
commit 65f3e74e35
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 406 additions and 429 deletions

View file

@ -8,7 +8,13 @@
package io.element.android.features.announcement.api package io.element.android.features.announcement.api
enum class Announcement { import androidx.compose.runtime.Immutable
Space,
NewNotificationSound, @Immutable
sealed interface Announcement {
enum class Fullscreen : Announcement {
Space,
}
data object NewNotificationSound : Announcement
} }

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl
import io.element.android.features.announcement.api.Announcement
sealed interface AnnouncementEvent {
data class Continue(
val announcement: Announcement.Fullscreen,
) : AnnouncementEvent
}

View file

@ -12,12 +12,16 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.features.announcement.api.Announcement import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.impl.store.AnnouncementStatus import io.element.android.features.announcement.impl.store.AnnouncementStatus
import io.element.android.features.announcement.impl.store.AnnouncementStore import io.element.android.features.announcement.impl.store.AnnouncementStore
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@Inject @Inject
class AnnouncementPresenter( class AnnouncementPresenter(
@ -25,13 +29,39 @@ class AnnouncementPresenter(
) : Presenter<AnnouncementState> { ) : Presenter<AnnouncementState> {
@Composable @Composable
override fun present(): AnnouncementState { override fun present(): AnnouncementState {
val showSpaceAnnouncement by remember { val coroutineScope = rememberCoroutineScope()
announcementStore.announcementStatusFlow(Announcement.Space).map {
it == AnnouncementStatus.Show val fullscreenAnnouncementToShow by remember {
combine(
flowOf(Unit),
announcementStore.announcementStatusFlow(Announcement.Fullscreen.Space).map {
it == AnnouncementStatus.Show
},
// Add other announcements here when needed
) { _, showFullscreenSpace ->
when {
showFullscreenSpace -> Announcement.Fullscreen.Space
else -> {
null
}
}
} }
}.collectAsState(false) }.collectAsState(null)
fun handle(event: AnnouncementEvent) {
when (event) {
is AnnouncementEvent.Continue -> coroutineScope.launch {
announcementStore.setAnnouncementStatus(
announcement = event.announcement,
status = AnnouncementStatus.Shown,
)
}
}
}
return AnnouncementState( return AnnouncementState(
showSpaceAnnouncement = showSpaceAnnouncement, announcement = fullscreenAnnouncementToShow,
eventSink = ::handle,
) )
} }
} }

View file

@ -8,12 +8,9 @@
package io.element.android.features.announcement.impl package io.element.android.features.announcement.impl
data class AnnouncementState( import io.element.android.features.announcement.api.Announcement
val showSpaceAnnouncement: Boolean,
)
fun anAnnouncementState( data class AnnouncementState(
showSpaceAnnouncement: Boolean = false, val announcement: Announcement.Fullscreen?,
) = AnnouncementState( val eventSink: (AnnouncementEvent) -> Unit,
showSpaceAnnouncement = showSpaceAnnouncement,
) )

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.announcement.api.Announcement
open class AnnouncementStateProvider : PreviewParameterProvider<AnnouncementState> {
override val values: Sequence<AnnouncementState>
get() = sequenceOf(
anAnnouncementState(),
anAnnouncementState(
announcement = Announcement.Fullscreen.Space,
),
)
}
fun anAnnouncementState(
announcement: Announcement.Fullscreen? = null,
eventSink: (AnnouncementEvent) -> Unit = {},
) = AnnouncementState(
announcement = announcement,
eventSink = eventSink,
)

View file

@ -8,35 +8,28 @@
package io.element.android.features.announcement.impl package io.element.android.features.announcement.impl
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.announcement.api.Announcement import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.announcement.api.AnnouncementService
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState import io.element.android.features.announcement.impl.fullscreen.FullscreenAnnouncementView
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementView
import io.element.android.features.announcement.impl.store.AnnouncementStatus import io.element.android.features.announcement.impl.store.AnnouncementStatus
import io.element.android.features.announcement.impl.store.AnnouncementStore import io.element.android.features.announcement.impl.store.AnnouncementStore
import io.element.android.libraries.architecture.Presenter
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultAnnouncementService( class DefaultAnnouncementService(
private val announcementStore: AnnouncementStore, private val announcementStore: AnnouncementStore,
private val announcementPresenter: Presenter<AnnouncementState>, private val announcementPresenter: AnnouncementPresenter,
private val spaceAnnouncementPresenter: Presenter<SpaceAnnouncementState>,
) : AnnouncementService { ) : AnnouncementService {
override suspend fun showAnnouncement(announcement: Announcement) { override suspend fun showAnnouncement(announcement: Announcement) {
when (announcement) { when (announcement) {
Announcement.Space -> showSpaceAnnouncement() is Announcement.Fullscreen -> showFullscreenAnnouncement(announcement)
Announcement.NewNotificationSound -> { Announcement.NewNotificationSound -> {
announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show) announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show)
} }
@ -49,13 +42,10 @@ class DefaultAnnouncementService(
override fun announcementsToShowFlow(): Flow<List<Announcement>> { override fun announcementsToShowFlow(): Flow<List<Announcement>> {
return combine( return combine(
announcementStore.announcementStatusFlow(Announcement.Space), flowOf(Unit),
announcementStore.announcementStatusFlow(Announcement.NewNotificationSound), announcementStore.announcementStatusFlow(Announcement.NewNotificationSound),
) { spaceAnnouncementStatus, newNotificationSoundStatus -> ) { _, newNotificationSoundStatus ->
buildList { buildList {
if (spaceAnnouncementStatus == AnnouncementStatus.Show) {
add(Announcement.Space)
}
if (newNotificationSoundStatus == AnnouncementStatus.Show) { if (newNotificationSoundStatus == AnnouncementStatus.Show) {
add(Announcement.NewNotificationSound) add(Announcement.NewNotificationSound)
} }
@ -63,27 +53,19 @@ class DefaultAnnouncementService(
} }
} }
private suspend fun showSpaceAnnouncement() { private suspend fun showFullscreenAnnouncement(announcement: Announcement.Fullscreen) {
val currentValue = announcementStore.announcementStatusFlow(Announcement.Space).first() val currentValue = announcementStore.announcementStatusFlow(announcement).first()
if (currentValue == AnnouncementStatus.NeverShown) { if (currentValue == AnnouncementStatus.NeverShown) {
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Show) announcementStore.setAnnouncementStatus(announcement, AnnouncementStatus.Show)
} }
} }
@Composable @Composable
override fun Render(modifier: Modifier) { override fun Render(modifier: Modifier) {
val announcementState = announcementPresenter.present() val announcementState = announcementPresenter.present()
Box(modifier = modifier.fillMaxSize()) { FullscreenAnnouncementView(
AnimatedVisibility( state = announcementState,
visible = announcementState.showSpaceAnnouncement, modifier = modifier,
enter = fadeIn(), )
exit = fadeOut(),
) {
val spaceAnnouncementState = spaceAnnouncementPresenter.present()
SpaceAnnouncementView(
state = spaceAnnouncementState,
)
}
}
} }
} }

View file

@ -1,29 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl.di
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.Binds
import dev.zacsweers.metro.ContributesTo
import io.element.android.features.announcement.impl.AnnouncementPresenter
import io.element.android.features.announcement.impl.AnnouncementState
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementPresenter
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState
import io.element.android.libraries.architecture.Presenter
@ContributesTo(AppScope::class)
@BindingContainer
interface AnnouncementModule {
@Binds
fun bindAnnouncementPresenter(presenter: AnnouncementPresenter): Presenter<AnnouncementState>
@Binds
fun bindSpaceAnnouncementPresenter(presenter: SpaceAnnouncementPresenter): Presenter<SpaceAnnouncementState>
}

View file

@ -0,0 +1,225 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl.fullscreen
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
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.announcement.api.Announcement
import io.element.android.features.announcement.impl.AnnouncementEvent
import io.element.android.features.announcement.impl.AnnouncementState
import io.element.android.features.announcement.impl.AnnouncementStateProvider
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.organisms.InfoListItem
import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrganism
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.BigIcon
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
/**
* Ref: https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Spaces-on-Element-X?node-id=4593-40181
*/
@Composable
fun FullscreenAnnouncementView(
state: AnnouncementState,
modifier: Modifier = Modifier,
) {
// Ensure that the content stays visible during the exit animation
var fullscreenAnnouncement by remember { mutableStateOf<Announcement.Fullscreen?>(null) }
if (state.announcement != null) {
fullscreenAnnouncement = state.announcement
}
Box(modifier = modifier.fillMaxSize()) {
AnimatedVisibility(
visible = state.announcement != null,
enter = fadeIn(),
exit = fadeOut(),
) {
fullscreenAnnouncement?.let {
FullscreenAnnouncementView(
announcement = it,
eventSink = state.eventSink,
)
}
}
}
}
@Composable
private fun FullscreenAnnouncementView(
announcement: Announcement.Fullscreen,
eventSink: (AnnouncementEvent) -> Unit,
modifier: Modifier = Modifier
) {
fun onContinue() {
eventSink(AnnouncementEvent.Continue(announcement))
}
BackHandler(onBack = ::onContinue)
HeaderFooterPage(
modifier = modifier,
isScrollable = true,
contentPadding = PaddingValues(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 24.dp),
header = {
FullscreenAnnouncementHeader(announcement)
},
content = {
FullscreenAnnouncementContent(
modifier = Modifier.padding(horizontal = 8.dp),
announcement = announcement,
)
},
footer = {
FullscreenAnnouncementFooter(
onContinue = ::onContinue,
)
}
)
}
@Composable
private fun FullscreenAnnouncementHeader(
announcement: Announcement.Fullscreen,
modifier: Modifier = Modifier,
) {
IconTitleSubtitleMolecule(
modifier = modifier.padding(top = 16.dp, bottom = 16.dp),
title = announcement.title(),
showBetaLabel = true,
subTitle = announcement.subtitle(),
iconStyle = BigIcon.Style.Default(
vectorIcon = announcement.icon(),
usePrimaryTint = true,
),
)
}
@Composable
private fun FullscreenAnnouncementContent(
announcement: Announcement.Fullscreen,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier.fillMaxSize(),
) {
InfoListOrganism(
modifier = Modifier.fillMaxWidth(),
items = announcement.items(),
textStyle = ElementTheme.typography.fontBodyLgMedium,
iconTint = ElementTheme.colors.iconSecondary,
iconSize = 24.dp
)
announcement.notice()?.let { notice ->
Text(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
text = notice,
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textSecondary,
textAlign = TextAlign.Center,
)
}
}
}
@Composable
private fun FullscreenAnnouncementFooter(
onContinue: () -> Unit,
) {
ButtonColumnMolecule(
modifier = Modifier.padding(bottom = 8.dp)
) {
Button(
text = stringResource(id = CommonStrings.action_continue),
onClick = onContinue,
modifier = Modifier.fillMaxWidth(),
)
}
}
@Composable
private fun Announcement.Fullscreen.title() = when (this) {
Announcement.Fullscreen.Space -> "Introducing Spaces"
}
@Composable
private fun Announcement.Fullscreen.subtitle() = when (this) {
Announcement.Fullscreen.Space -> "Welcome to the beta version of Spaces! With this first version you can:"
}
@Composable
private fun Announcement.Fullscreen.icon() = when (this) {
Announcement.Fullscreen.Space -> CompoundIcons.SpaceSolid()
}
@Composable
private fun Announcement.Fullscreen.items(): ImmutableList<InfoListItem> = when (this) {
Announcement.Fullscreen.Space -> persistentListOf(
InfoListItem(
message = "View spaces you\'ve created or joined",
iconVector = CompoundIcons.VisibilityOn(),
),
InfoListItem(
message = "Accept or decline invites to spaces",
iconVector = CompoundIcons.Email(),
),
InfoListItem(
message = "Discover any rooms you can join in your spaces",
iconVector = CompoundIcons.Search(),
),
InfoListItem(
message = "Join public spaces",
iconVector = CompoundIcons.Explore(),
),
InfoListItem(
message = "Leave any spaces youve joined",
iconVector = CompoundIcons.Leave(),
),
)
}
@Composable
private fun Announcement.Fullscreen.notice(): String? = when (this) {
Announcement.Fullscreen.Space -> "Filtering, creating and managing spaces is coming soon."
}
@PreviewsDayNight
@Composable
internal fun FullscreenAnnouncementViewPreview(@PreviewParameter(AnnouncementStateProvider::class) state: AnnouncementState) = ElementPreview {
FullscreenAnnouncementView(
state = state,
)
}

View file

@ -1,13 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl.spaces
sealed interface SpaceAnnouncementEvents {
data object Continue : SpaceAnnouncementEvents
}

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl.spaces
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import dev.zacsweers.metro.Inject
import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.impl.store.AnnouncementStatus
import io.element.android.features.announcement.impl.store.AnnouncementStore
import io.element.android.libraries.architecture.Presenter
import kotlinx.coroutines.launch
@Inject
class SpaceAnnouncementPresenter(
private val announcementStore: AnnouncementStore,
) : Presenter<SpaceAnnouncementState> {
@Composable
override fun present(): SpaceAnnouncementState {
val localCoroutineScope = rememberCoroutineScope()
fun handleEvent(event: SpaceAnnouncementEvents) {
when (event) {
SpaceAnnouncementEvents.Continue -> localCoroutineScope.launch {
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown)
}
}
}
return SpaceAnnouncementState(
eventSink = ::handleEvent,
)
}
}

View file

@ -1,13 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl.spaces
data class SpaceAnnouncementState(
val eventSink: (SpaceAnnouncementEvents) -> Unit
)

View file

@ -1,24 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl.spaces
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class SpaceAnnouncementStateProvider : PreviewParameterProvider<SpaceAnnouncementState> {
override val values: Sequence<SpaceAnnouncementState>
get() = sequenceOf(
aSpaceAnnouncementState(),
)
}
fun aSpaceAnnouncementState(
eventSink: (SpaceAnnouncementEvents) -> Unit = {},
) = SpaceAnnouncementState(
eventSink = eventSink,
)

View file

@ -1,158 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl.spaces
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
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.announcement.impl.R
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.organisms.InfoListItem
import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrganism
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.BigIcon
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.persistentListOf
/**
* Ref: https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Spaces-on-Element-X?node-id=4593-40181
*/
@Composable
fun SpaceAnnouncementView(
state: SpaceAnnouncementState,
modifier: Modifier = Modifier,
) {
val eventSink = state.eventSink
fun onContinue() {
eventSink(SpaceAnnouncementEvents.Continue)
}
BackHandler(onBack = ::onContinue)
HeaderFooterPage(
modifier = modifier,
isScrollable = true,
contentPadding = PaddingValues(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 24.dp),
header = {
SpaceAnnouncementHeader()
},
content = {
SpaceAnnouncementContent(
modifier = Modifier.padding(horizontal = 8.dp),
)
},
footer = {
SpaceAnnouncementFooter(
onContinue = ::onContinue,
)
}
)
}
@Composable
private fun SpaceAnnouncementHeader(
modifier: Modifier = Modifier,
) {
IconTitleSubtitleMolecule(
modifier = modifier.padding(top = 16.dp, bottom = 16.dp),
title = stringResource(id = R.string.screen_space_announcement_title),
showBetaLabel = true,
subTitle = stringResource(id = R.string.screen_space_announcement_subtitle),
iconStyle = BigIcon.Style.Default(
vectorIcon = CompoundIcons.SpaceSolid(),
usePrimaryTint = true,
),
)
}
@Composable
private fun SpaceAnnouncementContent(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier.fillMaxSize(),
) {
InfoListOrganism(
modifier = Modifier.fillMaxWidth(),
items = persistentListOf(
InfoListItem(
message = stringResource(id = R.string.screen_space_announcement_item1),
iconVector = CompoundIcons.VisibilityOn(),
),
InfoListItem(
message = stringResource(id = R.string.screen_space_announcement_item2),
iconVector = CompoundIcons.Email(),
),
InfoListItem(
message = stringResource(id = R.string.screen_space_announcement_item3),
iconVector = CompoundIcons.Search(),
),
InfoListItem(
message = stringResource(id = R.string.screen_space_announcement_item4),
iconVector = CompoundIcons.Explore(),
),
InfoListItem(
message = stringResource(id = R.string.screen_space_announcement_item5),
iconVector = CompoundIcons.Leave(),
),
),
textStyle = ElementTheme.typography.fontBodyLgMedium,
iconTint = ElementTheme.colors.iconSecondary,
iconSize = 24.dp
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
text = stringResource(id = R.string.screen_space_announcement_notice),
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textSecondary,
textAlign = TextAlign.Center,
)
}
}
@Composable
private fun SpaceAnnouncementFooter(
onContinue: () -> Unit,
) {
ButtonColumnMolecule(
modifier = Modifier.padding(bottom = 8.dp)
) {
Button(
text = stringResource(id = CommonStrings.action_continue),
onClick = onContinue,
modifier = Modifier.fillMaxWidth(),
)
}
}
@PreviewsDayNight
@Composable
internal fun SpaceAnnouncementViewPreview(@PreviewParameter(SpaceAnnouncementStateProvider::class) state: SpaceAnnouncementState) = ElementPreview {
SpaceAnnouncementView(
state = state,
)
}

View file

@ -35,9 +35,10 @@ class DefaultAnnouncementStore(
override fun announcementStatusFlow(announcement: Announcement): Flow<AnnouncementStatus> { override fun announcementStatusFlow(announcement: Announcement): Flow<AnnouncementStatus> {
val key = announcement.toKey() val key = announcement.toKey()
// Announcement.Fullscreen.Space is disabled, consider it's shown
// For NewNotificationSound, a migration will set it to Show on application upgrade (see AppMigration08) // For NewNotificationSound, a migration will set it to Show on application upgrade (see AppMigration08)
val defaultStatus = when (announcement) { val defaultStatus = when (announcement) {
Announcement.Space -> AnnouncementStatus.NeverShown Announcement.Fullscreen.Space -> AnnouncementStatus.Shown
Announcement.NewNotificationSound -> AnnouncementStatus.Shown Announcement.NewNotificationSound -> AnnouncementStatus.Shown
} }
return store.data.map { prefs -> return store.data.map { prefs ->
@ -52,6 +53,6 @@ class DefaultAnnouncementStore(
} }
private fun Announcement.toKey() = when (this) { private fun Announcement.toKey() = when (this) {
Announcement.Space -> spaceAnnouncementKey Announcement.Fullscreen.Space -> spaceAnnouncementKey
Announcement.NewNotificationSound -> newNotificationSoundKey Announcement.NewNotificationSound -> newNotificationSoundKey
} }

View file

@ -14,6 +14,7 @@ import io.element.android.features.announcement.impl.store.AnnouncementStatus
import io.element.android.features.announcement.impl.store.AnnouncementStore import io.element.android.features.announcement.impl.store.AnnouncementStore
import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore
import io.element.android.tests.testutils.test import io.element.android.tests.testutils.test
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@ -23,25 +24,47 @@ class AnnouncementPresenterTest {
val presenter = createAnnouncementPresenter() val presenter = createAnnouncementPresenter()
presenter.test { presenter.test {
val state = awaitItem() val state = awaitItem()
assertThat(state.showSpaceAnnouncement).isFalse() assertThat(state.announcement).isNull()
} }
} }
@Test @Test
fun `present - showSpaceAnnouncement value depends on the value in the store`() = runTest { fun `present - showFullscreen value depends on the value in the store`() = runTest {
val store = InMemoryAnnouncementStore() val store = InMemoryAnnouncementStore()
val presenter = createAnnouncementPresenter( val presenter = createAnnouncementPresenter(
announcementStore = store, announcementStore = store,
) )
presenter.test { presenter.test {
val state = awaitItem() val state = awaitItem()
assertThat(state.showSpaceAnnouncement).isFalse() assertThat(state.announcement).isNull()
store.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Show) store.setAnnouncementStatus(Announcement.Fullscreen.Space, AnnouncementStatus.Show)
val updatedState = awaitItem() val updatedState = awaitItem()
assertThat(updatedState.showSpaceAnnouncement).isTrue() assertThat(updatedState.announcement).isEqualTo(Announcement.Fullscreen.Space)
store.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown) store.setAnnouncementStatus(Announcement.Fullscreen.Space, AnnouncementStatus.Shown)
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.showSpaceAnnouncement).isFalse() assertThat(finalState.announcement).isNull()
}
}
@Test
fun `present - continue event will mark the announcement as Shown`() = runTest {
val store = InMemoryAnnouncementStore()
val presenter = createAnnouncementPresenter(
announcementStore = store,
)
presenter.test {
val state = awaitItem()
assertThat(state.announcement).isNull()
store.setAnnouncementStatus(Announcement.Fullscreen.Space, AnnouncementStatus.Show)
val statusShow = store.announcementStatusFlow(Announcement.Fullscreen.Space).first()
assertThat(statusShow).isEqualTo(AnnouncementStatus.Show)
val updatedState = awaitItem()
assertThat(updatedState.announcement).isEqualTo(Announcement.Fullscreen.Space)
updatedState.eventSink(AnnouncementEvent.Continue(Announcement.Fullscreen.Space))
val statusShown = store.announcementStatusFlow(Announcement.Fullscreen.Space).first()
assertThat(statusShown).isEqualTo(AnnouncementStatus.Shown)
val finalState = awaitItem()
assertThat(finalState.announcement).isNull()
} }
} }
} }

View file

@ -11,31 +11,28 @@ package io.element.android.features.announcement.impl
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.announcement.api.Announcement import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState
import io.element.android.features.announcement.impl.spaces.aSpaceAnnouncementState
import io.element.android.features.announcement.impl.store.AnnouncementStatus import io.element.android.features.announcement.impl.store.AnnouncementStatus
import io.element.android.features.announcement.impl.store.AnnouncementStore import io.element.android.features.announcement.impl.store.AnnouncementStore
import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore
import io.element.android.libraries.architecture.Presenter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
class DefaultAnnouncementServiceTest { class DefaultAnnouncementServiceTest {
@Test @Test
fun `when showing Space announcement, space announcement is set to show only if it was never shown`() = runTest { fun `when showing Fullscreen announcement, Fullscreen announcement is set to show only if it was never shown`() = runTest {
val announcementStore = InMemoryAnnouncementStore() val announcementStore = InMemoryAnnouncementStore()
val sut = createDefaultAnnouncementService( val sut = createDefaultAnnouncementService(
announcementStore = announcementStore, announcementStore = announcementStore,
) )
assertThat(announcementStore.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.NeverShown) assertThat(announcementStore.announcementStatusFlow(Announcement.Fullscreen.Space).first()).isEqualTo(AnnouncementStatus.NeverShown)
sut.showAnnouncement(Announcement.Space) sut.showAnnouncement(Announcement.Fullscreen.Space)
assertThat(announcementStore.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.Show) assertThat(announcementStore.announcementStatusFlow(Announcement.Fullscreen.Space).first()).isEqualTo(AnnouncementStatus.Show)
// Simulate user close the announcement // Simulate user close the announcement
sut.onAnnouncementDismissed(Announcement.Space) sut.onAnnouncementDismissed(Announcement.Fullscreen.Space)
// Entering again the space tab should not change the value // Entering again the space tab should not change the value
sut.showAnnouncement(Announcement.Space) sut.showAnnouncement(Announcement.Fullscreen.Space)
assertThat(announcementStore.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.Shown) assertThat(announcementStore.announcementStatusFlow(Announcement.Fullscreen.Space).first()).isEqualTo(AnnouncementStatus.Shown)
} }
@Test @Test
@ -62,11 +59,7 @@ class DefaultAnnouncementServiceTest {
) )
sut.announcementsToShowFlow().test { sut.announcementsToShowFlow().test {
assertThat(awaitItem()).isEmpty() assertThat(awaitItem()).isEmpty()
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Show)
assertThat(awaitItem()).containsExactly(Announcement.Space)
announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show) announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show)
assertThat(awaitItem()).containsExactly(Announcement.Space, Announcement.NewNotificationSound)
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown)
assertThat(awaitItem()).containsExactly(Announcement.NewNotificationSound) assertThat(awaitItem()).containsExactly(Announcement.NewNotificationSound)
announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Shown) announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Shown)
assertThat(awaitItem()).isEmpty() assertThat(awaitItem()).isEmpty()
@ -75,11 +68,9 @@ class DefaultAnnouncementServiceTest {
private fun createDefaultAnnouncementService( private fun createDefaultAnnouncementService(
announcementStore: AnnouncementStore = InMemoryAnnouncementStore(), announcementStore: AnnouncementStore = InMemoryAnnouncementStore(),
announcementPresenter: Presenter<AnnouncementState> = Presenter { anAnnouncementState() }, announcementPresenter: AnnouncementPresenter = AnnouncementPresenter(announcementStore),
spaceAnnouncementPresenter: Presenter<SpaceAnnouncementState> = Presenter { aSpaceAnnouncementState() },
) = DefaultAnnouncementService( ) = DefaultAnnouncementService(
announcementStore = announcementStore, announcementStore = announcementStore,
announcementPresenter = announcementPresenter, announcementPresenter = announcementPresenter,
spaceAnnouncementPresenter = spaceAnnouncementPresenter,
) )
} }

View file

@ -6,12 +6,16 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.features.announcement.impl.spaces package io.element.android.features.announcement.impl.fullscreen
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.impl.AnnouncementEvent
import io.element.android.features.announcement.impl.AnnouncementState
import io.element.android.features.announcement.impl.anAnnouncementState
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.clickOn
@ -22,39 +26,41 @@ import org.junit.rules.TestRule
import org.junit.runner.RunWith import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class SpaceAnnouncementViewTest { class FullscreenAnnouncementViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>() @get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test @Test
fun `clicking on back sends a SpaceAnnouncementEvents`() { fun `clicking on back sends a AnnouncementEvent`() {
val eventsRecorder = EventsRecorder<SpaceAnnouncementEvents>() val eventsRecorder = EventsRecorder<AnnouncementEvent>()
rule.setSpaceAnnouncementView( rule.setFullscreenAnnouncementView(
aSpaceAnnouncementState( anAnnouncementState(
announcement = Announcement.Fullscreen.Space,
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
) )
rule.pressBackKey() rule.pressBackKey()
eventsRecorder.assertSingle(SpaceAnnouncementEvents.Continue) eventsRecorder.assertSingle(AnnouncementEvent.Continue(Announcement.Fullscreen.Space))
} }
@Test @Test
fun `clicking on Continue sends a SpaceAnnouncementEvents`() { fun `clicking on Continue sends a AnnouncementEvent`() {
val eventsRecorder = EventsRecorder<SpaceAnnouncementEvents>() val eventsRecorder = EventsRecorder<AnnouncementEvent>()
rule.setSpaceAnnouncementView( rule.setFullscreenAnnouncementView(
aSpaceAnnouncementState( anAnnouncementState(
announcement = Announcement.Fullscreen.Space,
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
) )
rule.clickOn(CommonStrings.action_continue) rule.clickOn(CommonStrings.action_continue)
eventsRecorder.assertSingle(SpaceAnnouncementEvents.Continue) eventsRecorder.assertSingle(AnnouncementEvent.Continue(Announcement.Fullscreen.Space))
} }
} }
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setSpaceAnnouncementView( private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setFullscreenAnnouncementView(
state: SpaceAnnouncementState, state: AnnouncementState,
) { ) {
setContent { setContent {
SpaceAnnouncementView( FullscreenAnnouncementView(
state = state, state = state,
) )
} }

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.announcement.impl.spaces
import com.google.common.truth.Truth.assertThat
import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.impl.store.AnnouncementStatus
import io.element.android.features.announcement.impl.store.AnnouncementStore
import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore
import io.element.android.tests.testutils.test
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.Test
class SpaceAnnouncementPresenterTest {
@Test
fun `present - when user continues, the store is updated`() = runTest {
val store = InMemoryAnnouncementStore()
val presenter = createSpaceAnnouncementPresenter(
announcementStore = store,
)
presenter.test {
assertThat(store.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.NeverShown)
val state = awaitItem()
state.eventSink(SpaceAnnouncementEvents.Continue)
assertThat(store.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.Shown)
}
}
}
private fun createSpaceAnnouncementPresenter(
announcementStore: AnnouncementStore = InMemoryAnnouncementStore(),
) = SpaceAnnouncementPresenter(
announcementStore = announcementStore,
)

View file

@ -14,10 +14,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
class InMemoryAnnouncementStore( class InMemoryAnnouncementStore(
initialSpaceAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown, initialFullscreenAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown,
initialNewNotificationSoundAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown, initialNewNotificationSoundAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown,
) : AnnouncementStore { ) : AnnouncementStore {
private val spaceAnnouncement = MutableStateFlow(initialSpaceAnnouncementStatus) private val fullScreenAnnouncement = MutableStateFlow(initialFullscreenAnnouncementStatus)
private val newNotificationSoundAnnouncement = MutableStateFlow(initialNewNotificationSoundAnnouncementStatus) private val newNotificationSoundAnnouncement = MutableStateFlow(initialNewNotificationSoundAnnouncementStatus)
override suspend fun setAnnouncementStatus(announcement: Announcement, status: AnnouncementStatus) { override suspend fun setAnnouncementStatus(announcement: Announcement, status: AnnouncementStatus) {
@ -29,12 +29,12 @@ class InMemoryAnnouncementStore(
} }
override suspend fun reset() { override suspend fun reset() {
spaceAnnouncement.value = AnnouncementStatus.NeverShown fullScreenAnnouncement.value = AnnouncementStatus.NeverShown
newNotificationSoundAnnouncement.value = AnnouncementStatus.NeverShown newNotificationSoundAnnouncement.value = AnnouncementStatus.NeverShown
} }
private fun Announcement.toMutableStateFlow() = when (this) { private fun Announcement.toMutableStateFlow() = when (this) {
Announcement.Space -> spaceAnnouncement is Announcement.Fullscreen -> fullScreenAnnouncement
Announcement.NewNotificationSound -> newNotificationSoundAnnouncement Announcement.NewNotificationSound -> newNotificationSoundAnnouncement
} }
} }

View file

@ -19,8 +19,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.api.AnnouncementService
import io.element.android.features.home.impl.roomlist.RoomListState import io.element.android.features.home.impl.roomlist.RoomListState
import io.element.android.features.home.impl.spaces.HomeSpacesState import io.element.android.features.home.impl.spaces.HomeSpacesState
import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.features.logout.api.direct.DirectLogoutState
@ -47,7 +45,6 @@ class HomePresenter(
private val logoutPresenter: Presenter<DirectLogoutState>, private val logoutPresenter: Presenter<DirectLogoutState>,
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability, private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
private val sessionStore: SessionStore, private val sessionStore: SessionStore,
private val announcementService: AnnouncementService,
) : Presenter<HomeState> { ) : Presenter<HomeState> {
private val currentUserWithNeighborsBuilder = CurrentUserWithNeighborsBuilder() private val currentUserWithNeighborsBuilder = CurrentUserWithNeighborsBuilder()
@ -82,10 +79,7 @@ class HomePresenter(
fun handleEvent(event: HomeEvent) { fun handleEvent(event: HomeEvent) {
when (event) { when (event) {
is HomeEvent.SelectHomeNavigationBarItem -> coroutineState.launch { is HomeEvent.SelectHomeNavigationBarItem -> {
if (event.item == HomeNavigationBarItem.Spaces) {
announcementService.showAnnouncement(Announcement.Space)
}
currentHomeNavigationBarItemOrdinal = event.item.ordinal currentHomeNavigationBarItemOrdinal = event.item.ordinal
} }
is HomeEvent.SwitchToAccount -> coroutineState.launch { is HomeEvent.SwitchToAccount -> coroutineState.launch {

View file

@ -9,14 +9,11 @@
package io.element.android.features.home.impl package io.element.android.features.home.impl
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.api.AnnouncementService
import io.element.android.features.home.impl.roomlist.aRoomListState import io.element.android.features.home.impl.roomlist.aRoomListState
import io.element.android.features.home.impl.spaces.HomeSpacesState import io.element.android.features.home.impl.spaces.HomeSpacesState
import io.element.android.features.home.impl.spaces.aHomeSpacesState import io.element.android.features.home.impl.spaces.aHomeSpacesState
import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.features.logout.api.direct.aDirectLogoutState
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
import io.element.android.features.rageshake.test.logs.FakeAnnouncementService
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.indicator.api.IndicatorService
@ -34,8 +31,6 @@ import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
import io.element.android.libraries.sessionstorage.test.aSessionData import io.element.android.libraries.sessionstorage.test.aSessionData
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.test import io.element.android.tests.testutils.test
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -137,14 +132,10 @@ class HomePresenterTest {
@Test @Test
fun `present - NavigationBar change`() = runTest { fun `present - NavigationBar change`() = runTest {
val showAnnouncementResult = lambdaRecorder<Announcement, Unit> { }
val presenter = createHomePresenter( val presenter = createHomePresenter(
sessionStore = InMemorySessionStore( sessionStore = InMemorySessionStore(
updateUserProfileResult = { _, _, _ -> }, updateUserProfileResult = { _, _, _ -> },
), ),
announcementService = FakeAnnouncementService(
showAnnouncementResult = showAnnouncementResult,
)
) )
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
@ -152,8 +143,6 @@ class HomePresenterTest {
initialState.eventSink(HomeEvent.SelectHomeNavigationBarItem(HomeNavigationBarItem.Spaces)) initialState.eventSink(HomeEvent.SelectHomeNavigationBarItem(HomeNavigationBarItem.Spaces))
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Spaces) assertThat(finalState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Spaces)
showAnnouncementResult.assertions().isCalledOnce()
.with(value(Announcement.Space))
} }
} }
} }
@ -166,7 +155,6 @@ internal fun createHomePresenter(
indicatorService: IndicatorService = FakeIndicatorService(), indicatorService: IndicatorService = FakeIndicatorService(),
homeSpacesPresenter: Presenter<HomeSpacesState> = Presenter { aHomeSpacesState() }, homeSpacesPresenter: Presenter<HomeSpacesState> = Presenter { aHomeSpacesState() },
sessionStore: SessionStore = InMemorySessionStore(), sessionStore: SessionStore = InMemorySessionStore(),
announcementService: AnnouncementService = FakeAnnouncementService(),
) = HomePresenter( ) = HomePresenter(
client = client, client = client,
syncService = syncService, syncService = syncService,
@ -177,5 +165,4 @@ internal fun createHomePresenter(
logoutPresenter = { aDirectLogoutState() }, logoutPresenter = { aDirectLogoutState() },
rageshakeFeatureAvailability = rageshakeFeatureAvailability, rageshakeFeatureAvailability = rageshakeFeatureAvailability,
sessionStore = sessionStore, sessionStore = sessionStore,
announcementService = announcementService,
) )

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:96a867cb12498cbdc97957bee07855dfaa13602baddaf933aff2b666ef4c7650
size 3642

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5bb36ccd718f3fec5b04f1bc812dc7718b5ea7fa4619c8b031466297a8d016fd
size 3659