Skip to content

Commit 97ae89e

Browse files
authored
Merge pull request #5451 from element-hq/feature/bma/spaceAnnoucement
Space annoucement
2 parents 0c63d0c + cfe7d94 commit 97ae89e

File tree

37 files changed

+945
-13
lines changed

37 files changed

+945
-13
lines changed

appnav/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ dependencies {
4444

4545
implementation(libs.coil)
4646

47+
implementation(projects.features.announcement.api)
4748
implementation(projects.features.ftue.api)
4849
implementation(projects.features.share.api)
4950

appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import io.element.android.appnav.intent.ResolvedIntent
3434
import io.element.android.appnav.root.RootNavStateFlowFactory
3535
import io.element.android.appnav.root.RootPresenter
3636
import io.element.android.appnav.root.RootView
37+
import io.element.android.features.announcement.api.AnnouncementService
3738
import io.element.android.features.login.api.LoginParams
3839
import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl
3940
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
@@ -64,7 +65,9 @@ import kotlinx.coroutines.launch
6465
import kotlinx.parcelize.Parcelize
6566
import timber.log.Timber
6667

67-
@ContributesNode(AppScope::class) @AssistedInject class RootFlowNode(
68+
@ContributesNode(AppScope::class)
69+
@AssistedInject
70+
class RootFlowNode(
6871
@Assisted val buildContext: BuildContext,
6972
@Assisted plugins: List<Plugin>,
7073
private val sessionStore: SessionStore,
@@ -79,6 +82,7 @@ import timber.log.Timber
7982
private val oidcActionFlow: OidcActionFlow,
8083
private val bugReporter: BugReporter,
8184
private val featureFlagService: FeatureFlagService,
85+
private val announcementService: AnnouncementService,
8286
) : BaseFlowNode<RootFlowNode.NavTarget>(
8387
backstack = BackStack(
8488
initialElement = NavTarget.SplashScreen,
@@ -185,6 +189,7 @@ import timber.log.Timber
185189
}
186190
}
187191
BackstackView(transitionHandler = transitionHandler)
192+
announcementService.Render(Modifier)
188193
}
189194
}
190195

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
plugins {
8+
id("io.element.android-compose-library")
9+
}
10+
11+
android {
12+
namespace = "io.element.android.features.announcement.api"
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.announcement.api
9+
10+
enum class Announcement {
11+
Space,
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.announcement.api
9+
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.ui.Modifier
12+
13+
interface AnnouncementService {
14+
suspend fun showAnnouncement(announcement: Announcement)
15+
16+
@Composable
17+
fun Render(
18+
modifier: Modifier,
19+
)
20+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import extension.setupDependencyInjection
2+
import extension.testCommonDependencies
3+
4+
/*
5+
* Copyright 2025 New Vector Ltd.
6+
*
7+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
8+
* Please see LICENSE files in the repository root for full details.
9+
*/
10+
11+
plugins {
12+
id("io.element.android-compose-library")
13+
}
14+
15+
android {
16+
namespace = "io.element.android.features.announcement.impl"
17+
18+
testOptions {
19+
unitTests {
20+
isIncludeAndroidResources = true
21+
}
22+
}
23+
}
24+
25+
setupDependencyInjection()
26+
27+
dependencies {
28+
implementation(projects.libraries.architecture)
29+
implementation(projects.libraries.designsystem)
30+
implementation(projects.libraries.preferences.api)
31+
implementation(projects.libraries.uiStrings)
32+
api(projects.features.announcement.api)
33+
implementation(libs.androidx.datastore.preferences)
34+
35+
testCommonDependencies(libs, true)
36+
testImplementation(projects.libraries.matrix.test)
37+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.announcement.impl
9+
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.runtime.collectAsState
12+
import androidx.compose.runtime.getValue
13+
import androidx.compose.runtime.remember
14+
import dev.zacsweers.metro.Inject
15+
import io.element.android.features.announcement.impl.store.AnnouncementStore
16+
import io.element.android.libraries.architecture.Presenter
17+
import kotlinx.coroutines.flow.map
18+
19+
@Inject
20+
class AnnouncementPresenter(
21+
private val announcementStore: AnnouncementStore,
22+
) : Presenter<AnnouncementState> {
23+
@Composable
24+
override fun present(): AnnouncementState {
25+
val showSpaceAnnouncement by remember {
26+
announcementStore.spaceAnnouncementFlow().map {
27+
it == AnnouncementStore.SpaceAnnouncement.Show
28+
}
29+
}.collectAsState(false)
30+
return AnnouncementState(
31+
showSpaceAnnouncement = showSpaceAnnouncement,
32+
)
33+
}
34+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.announcement.impl
9+
10+
data class AnnouncementState(
11+
val showSpaceAnnouncement: Boolean,
12+
)
13+
14+
fun anAnnouncementState(
15+
showSpaceAnnouncement: Boolean = false,
16+
) = AnnouncementState(
17+
showSpaceAnnouncement = showSpaceAnnouncement,
18+
)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.announcement.impl
9+
10+
import androidx.compose.animation.AnimatedVisibility
11+
import androidx.compose.animation.fadeIn
12+
import androidx.compose.animation.fadeOut
13+
import androidx.compose.foundation.layout.Box
14+
import androidx.compose.foundation.layout.fillMaxSize
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.ui.Modifier
17+
import dev.zacsweers.metro.AppScope
18+
import dev.zacsweers.metro.ContributesBinding
19+
import dev.zacsweers.metro.Inject
20+
import io.element.android.features.announcement.api.Announcement
21+
import io.element.android.features.announcement.api.AnnouncementService
22+
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState
23+
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementView
24+
import io.element.android.features.announcement.impl.store.AnnouncementStore
25+
import io.element.android.libraries.architecture.Presenter
26+
import kotlinx.coroutines.flow.first
27+
28+
@ContributesBinding(AppScope::class)
29+
@Inject
30+
class DefaultAnnouncementService(
31+
private val announcementStore: AnnouncementStore,
32+
private val announcementPresenter: Presenter<AnnouncementState>,
33+
private val spaceAnnouncementPresenter: Presenter<SpaceAnnouncementState>,
34+
) : AnnouncementService {
35+
override suspend fun showAnnouncement(announcement: Announcement) {
36+
when (announcement) {
37+
Announcement.Space -> showSpaceAnnouncement()
38+
}
39+
}
40+
41+
private suspend fun showSpaceAnnouncement() {
42+
val currentValue = announcementStore.spaceAnnouncementFlow().first()
43+
if (currentValue == AnnouncementStore.SpaceAnnouncement.NeverShown) {
44+
announcementStore.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Show)
45+
}
46+
}
47+
48+
@Composable
49+
override fun Render(modifier: Modifier) {
50+
val announcementState = announcementPresenter.present()
51+
Box(modifier = modifier.fillMaxSize()) {
52+
AnimatedVisibility(
53+
visible = announcementState.showSpaceAnnouncement,
54+
enter = fadeIn(),
55+
exit = fadeOut(),
56+
) {
57+
val spaceAnnouncementState = spaceAnnouncementPresenter.present()
58+
SpaceAnnouncementView(
59+
state = spaceAnnouncementState,
60+
)
61+
}
62+
}
63+
}
64+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.announcement.impl.di
9+
10+
import dev.zacsweers.metro.AppScope
11+
import dev.zacsweers.metro.BindingContainer
12+
import dev.zacsweers.metro.Binds
13+
import dev.zacsweers.metro.ContributesTo
14+
import io.element.android.features.announcement.impl.AnnouncementPresenter
15+
import io.element.android.features.announcement.impl.AnnouncementState
16+
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementPresenter
17+
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState
18+
import io.element.android.libraries.architecture.Presenter
19+
20+
@ContributesTo(AppScope::class)
21+
@BindingContainer
22+
interface AnnouncementModule {
23+
@Binds
24+
fun bindAnnouncementPresenter(presenter: AnnouncementPresenter): Presenter<AnnouncementState>
25+
26+
@Binds
27+
fun bindSpaceAnnouncementPresenter(presenter: SpaceAnnouncementPresenter): Presenter<SpaceAnnouncementState>
28+
}

0 commit comments

Comments
 (0)