Merge pull request #6561 from element-hq/feature/bma/removeSpaceAnnouncement
Remove space announcement
This commit is contained in:
commit
e9dd3115b8
25 changed files with 406 additions and 429 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
@ -12,12 +12,16 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
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.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Inject
|
||||
class AnnouncementPresenter(
|
||||
|
|
@ -25,13 +29,39 @@ class AnnouncementPresenter(
|
|||
) : Presenter<AnnouncementState> {
|
||||
@Composable
|
||||
override fun present(): AnnouncementState {
|
||||
val showSpaceAnnouncement by remember {
|
||||
announcementStore.announcementStatusFlow(Announcement.Space).map {
|
||||
it == AnnouncementStatus.Show
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
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(
|
||||
showSpaceAnnouncement = showSpaceAnnouncement,
|
||||
announcement = fullscreenAnnouncementToShow,
|
||||
eventSink = ::handle,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,9 @@
|
|||
|
||||
package io.element.android.features.announcement.impl
|
||||
|
||||
data class AnnouncementState(
|
||||
val showSpaceAnnouncement: Boolean,
|
||||
)
|
||||
import io.element.android.features.announcement.api.Announcement
|
||||
|
||||
fun anAnnouncementState(
|
||||
showSpaceAnnouncement: Boolean = false,
|
||||
) = AnnouncementState(
|
||||
showSpaceAnnouncement = showSpaceAnnouncement,
|
||||
data class AnnouncementState(
|
||||
val announcement: Announcement.Fullscreen?,
|
||||
val eventSink: (AnnouncementEvent) -> Unit,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -8,35 +8,28 @@
|
|||
|
||||
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.ui.Modifier
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.announcement.api.Announcement
|
||||
import io.element.android.features.announcement.api.AnnouncementService
|
||||
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState
|
||||
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementView
|
||||
import io.element.android.features.announcement.impl.fullscreen.FullscreenAnnouncementView
|
||||
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.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultAnnouncementService(
|
||||
private val announcementStore: AnnouncementStore,
|
||||
private val announcementPresenter: Presenter<AnnouncementState>,
|
||||
private val spaceAnnouncementPresenter: Presenter<SpaceAnnouncementState>,
|
||||
private val announcementPresenter: AnnouncementPresenter,
|
||||
) : AnnouncementService {
|
||||
override suspend fun showAnnouncement(announcement: Announcement) {
|
||||
when (announcement) {
|
||||
Announcement.Space -> showSpaceAnnouncement()
|
||||
is Announcement.Fullscreen -> showFullscreenAnnouncement(announcement)
|
||||
Announcement.NewNotificationSound -> {
|
||||
announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show)
|
||||
}
|
||||
|
|
@ -49,13 +42,10 @@ class DefaultAnnouncementService(
|
|||
|
||||
override fun announcementsToShowFlow(): Flow<List<Announcement>> {
|
||||
return combine(
|
||||
announcementStore.announcementStatusFlow(Announcement.Space),
|
||||
flowOf(Unit),
|
||||
announcementStore.announcementStatusFlow(Announcement.NewNotificationSound),
|
||||
) { spaceAnnouncementStatus, newNotificationSoundStatus ->
|
||||
) { _, newNotificationSoundStatus ->
|
||||
buildList {
|
||||
if (spaceAnnouncementStatus == AnnouncementStatus.Show) {
|
||||
add(Announcement.Space)
|
||||
}
|
||||
if (newNotificationSoundStatus == AnnouncementStatus.Show) {
|
||||
add(Announcement.NewNotificationSound)
|
||||
}
|
||||
|
|
@ -63,27 +53,19 @@ class DefaultAnnouncementService(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun showSpaceAnnouncement() {
|
||||
val currentValue = announcementStore.announcementStatusFlow(Announcement.Space).first()
|
||||
private suspend fun showFullscreenAnnouncement(announcement: Announcement.Fullscreen) {
|
||||
val currentValue = announcementStore.announcementStatusFlow(announcement).first()
|
||||
if (currentValue == AnnouncementStatus.NeverShown) {
|
||||
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Show)
|
||||
announcementStore.setAnnouncementStatus(announcement, AnnouncementStatus.Show)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Render(modifier: Modifier) {
|
||||
val announcementState = announcementPresenter.present()
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
AnimatedVisibility(
|
||||
visible = announcementState.showSpaceAnnouncement,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
val spaceAnnouncementState = spaceAnnouncementPresenter.present()
|
||||
SpaceAnnouncementView(
|
||||
state = spaceAnnouncementState,
|
||||
)
|
||||
}
|
||||
}
|
||||
FullscreenAnnouncementView(
|
||||
state = announcementState,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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 you’ve 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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -35,9 +35,10 @@ class DefaultAnnouncementStore(
|
|||
|
||||
override fun announcementStatusFlow(announcement: Announcement): Flow<AnnouncementStatus> {
|
||||
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)
|
||||
val defaultStatus = when (announcement) {
|
||||
Announcement.Space -> AnnouncementStatus.NeverShown
|
||||
Announcement.Fullscreen.Space -> AnnouncementStatus.Shown
|
||||
Announcement.NewNotificationSound -> AnnouncementStatus.Shown
|
||||
}
|
||||
return store.data.map { prefs ->
|
||||
|
|
@ -52,6 +53,6 @@ class DefaultAnnouncementStore(
|
|||
}
|
||||
|
||||
private fun Announcement.toKey() = when (this) {
|
||||
Announcement.Space -> spaceAnnouncementKey
|
||||
Announcement.Fullscreen.Space -> spaceAnnouncementKey
|
||||
Announcement.NewNotificationSound -> newNotificationSoundKey
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue