Skip to content

Commit 752e846

Browse files
committed
Improve AnnouncementService.
1 parent 59ef782 commit 752e846

File tree

20 files changed

+169
-81
lines changed

20 files changed

+169
-81
lines changed

features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/Announcement.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ package io.element.android.features.announcement.api
99

1010
enum class Announcement {
1111
Space,
12+
NewNotificationSound,
1213
}

features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/AnnouncementService.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,18 @@ package io.element.android.features.announcement.api
99

1010
import androidx.compose.runtime.Composable
1111
import androidx.compose.ui.Modifier
12+
import kotlinx.coroutines.flow.Flow
1213

1314
interface AnnouncementService {
1415
suspend fun showAnnouncement(announcement: Announcement)
1516

17+
suspend fun onAnnouncementDismissed(announcement: Announcement)
18+
19+
fun announcementsToShowFlow(): Flow<List<Announcement>>
20+
21+
/**
22+
* Use this composable to render the announcement UI in Fullscreen.
23+
*/
1624
@Composable
1725
fun Render(
1826
modifier: Modifier,

features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementPresenter.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.compose.runtime.collectAsState
1212
import androidx.compose.runtime.getValue
1313
import androidx.compose.runtime.remember
1414
import dev.zacsweers.metro.Inject
15+
import io.element.android.features.announcement.api.Announcement
1516
import io.element.android.features.announcement.impl.store.AnnouncementStore
1617
import io.element.android.libraries.architecture.Presenter
1718
import kotlinx.coroutines.flow.map
@@ -23,8 +24,8 @@ class AnnouncementPresenter(
2324
@Composable
2425
override fun present(): AnnouncementState {
2526
val showSpaceAnnouncement by remember {
26-
announcementStore.spaceAnnouncementFlow().map {
27-
it == AnnouncementStore.SpaceAnnouncement.Show
27+
announcementStore.announcementStateFlow(Announcement.Space).map {
28+
it == AnnouncementStore.AnnouncementStatus.Show
2829
}
2930
}.collectAsState(false)
3031
return AnnouncementState(

features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementSta
2323
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementView
2424
import io.element.android.features.announcement.impl.store.AnnouncementStore
2525
import io.element.android.libraries.architecture.Presenter
26+
import kotlinx.coroutines.flow.Flow
27+
import kotlinx.coroutines.flow.combine
2628
import kotlinx.coroutines.flow.first
2729

2830
@ContributesBinding(AppScope::class)
@@ -35,13 +37,36 @@ class DefaultAnnouncementService(
3537
override suspend fun showAnnouncement(announcement: Announcement) {
3638
when (announcement) {
3739
Announcement.Space -> showSpaceAnnouncement()
40+
Announcement.NewNotificationSound -> {
41+
announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStore.AnnouncementStatus.Show)
42+
}
43+
}
44+
}
45+
46+
override suspend fun onAnnouncementDismissed(announcement: Announcement) {
47+
announcementStore.setAnnouncementStatus(announcement, AnnouncementStore.AnnouncementStatus.Shown)
48+
}
49+
50+
override fun announcementsToShowFlow(): Flow<List<Announcement>> {
51+
return combine(
52+
announcementStore.announcementStateFlow(Announcement.Space),
53+
announcementStore.announcementStateFlow(Announcement.NewNotificationSound),
54+
) { spaceAnnouncementStatus, newNotificationSoundStatus ->
55+
buildList {
56+
if (spaceAnnouncementStatus == AnnouncementStore.AnnouncementStatus.Show) {
57+
add(Announcement.Space)
58+
}
59+
if (newNotificationSoundStatus == AnnouncementStore.AnnouncementStatus.Show) {
60+
add(Announcement.NewNotificationSound)
61+
}
62+
}
3863
}
3964
}
4065

4166
private suspend fun showSpaceAnnouncement() {
42-
val currentValue = announcementStore.spaceAnnouncementFlow().first()
43-
if (currentValue == AnnouncementStore.SpaceAnnouncement.NeverShown) {
44-
announcementStore.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Show)
67+
val currentValue = announcementStore.announcementStateFlow(Announcement.Space).first()
68+
if (currentValue == AnnouncementStore.AnnouncementStatus.NeverShown) {
69+
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStore.AnnouncementStatus.Show)
4570
}
4671
}
4772

features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenter.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ package io.element.android.features.announcement.impl.spaces
1010
import androidx.compose.runtime.Composable
1111
import androidx.compose.runtime.rememberCoroutineScope
1212
import dev.zacsweers.metro.Inject
13+
import io.element.android.features.announcement.api.Announcement
1314
import io.element.android.features.announcement.impl.store.AnnouncementStore
14-
import io.element.android.features.announcement.impl.store.AnnouncementStore.SpaceAnnouncement
15+
import io.element.android.features.announcement.impl.store.AnnouncementStore.AnnouncementStatus
1516
import io.element.android.libraries.architecture.Presenter
1617
import kotlinx.coroutines.launch
1718

@@ -26,7 +27,7 @@ class SpaceAnnouncementPresenter(
2627
fun handleEvents(event: SpaceAnnouncementEvents) {
2728
when (event) {
2829
SpaceAnnouncementEvents.Continue -> localCoroutineScope.launch {
29-
announcementStore.setSpaceAnnouncementValue(SpaceAnnouncement.Shown)
30+
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown)
3031
}
3132
}
3233
}

features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/AnnouncementStore.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,22 @@
77

88
package io.element.android.features.announcement.impl.store
99

10+
import io.element.android.features.announcement.api.Announcement
1011
import kotlinx.coroutines.flow.Flow
1112

1213
interface AnnouncementStore {
13-
suspend fun setSpaceAnnouncementValue(value: SpaceAnnouncement)
14-
fun spaceAnnouncementFlow(): Flow<SpaceAnnouncement>
14+
suspend fun setAnnouncementStatus(
15+
announcement: Announcement,
16+
status: AnnouncementStatus,
17+
)
18+
19+
fun announcementStateFlow(
20+
announcement: Announcement,
21+
): Flow<AnnouncementStatus>
1522

1623
suspend fun reset()
1724

18-
enum class SpaceAnnouncement {
25+
enum class AnnouncementStatus {
1926
NeverShown,
2027
Show,
2128
Shown,

features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import androidx.datastore.preferences.core.intPreferencesKey
1212
import dev.zacsweers.metro.AppScope
1313
import dev.zacsweers.metro.ContributesBinding
1414
import dev.zacsweers.metro.Inject
15+
import io.element.android.features.announcement.api.Announcement
1516
import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory
1617
import kotlinx.coroutines.flow.Flow
1718
import kotlinx.coroutines.flow.map
1819

1920
private val spaceAnnouncementKey = intPreferencesKey("spaceAnnouncement")
21+
private val showNewNotificationSoundBannerKey = intPreferencesKey("showNewNotificationSoundBanner")
2022

2123
@ContributesBinding(AppScope::class)
2224
@Inject
@@ -25,20 +27,32 @@ class DefaultAnnouncementStore(
2527
) : AnnouncementStore {
2628
private val store = preferenceDataStoreFactory.create("elementx_announcement")
2729

28-
override suspend fun setSpaceAnnouncementValue(value: AnnouncementStore.SpaceAnnouncement) {
29-
store.edit {
30-
it[spaceAnnouncementKey] = value.ordinal
30+
override suspend fun setAnnouncementStatus(announcement: Announcement, status: AnnouncementStore.AnnouncementStatus) {
31+
val key = announcement.toKey()
32+
store.edit { prefs ->
33+
prefs[key] = status.ordinal
3134
}
3235
}
3336

34-
override fun spaceAnnouncementFlow(): Flow<AnnouncementStore.SpaceAnnouncement> {
37+
override fun announcementStateFlow(announcement: Announcement): Flow<AnnouncementStore.AnnouncementStatus> {
38+
val key = announcement.toKey()
39+
// For NewNotificationSound, a migration will set it to Show on application upgrade (see AppMigration08)
40+
val defaultStatus = when (announcement) {
41+
Announcement.Space -> AnnouncementStore.AnnouncementStatus.NeverShown
42+
Announcement.NewNotificationSound -> AnnouncementStore.AnnouncementStatus.Shown
43+
}
3544
return store.data.map { prefs ->
36-
val ordinal = prefs[spaceAnnouncementKey] ?: AnnouncementStore.SpaceAnnouncement.NeverShown.ordinal
37-
AnnouncementStore.SpaceAnnouncement.entries.getOrElse(ordinal) { AnnouncementStore.SpaceAnnouncement.NeverShown }
45+
val ordinal = prefs[key] ?: defaultStatus.ordinal
46+
AnnouncementStore.AnnouncementStatus.entries.getOrElse(ordinal) { defaultStatus }
3847
}
3948
}
4049

4150
override suspend fun reset() {
4251
store.edit { it.clear() }
4352
}
4453
}
54+
55+
private fun Announcement.toKey() = when (this) {
56+
Announcement.Space -> spaceAnnouncementKey
57+
Announcement.NewNotificationSound -> showNewNotificationSoundBannerKey
58+
}

features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/AnnouncementPresenterTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package io.element.android.features.announcement.impl
99

1010
import com.google.common.truth.Truth.assertThat
11+
import io.element.android.features.announcement.api.Announcement
1112
import io.element.android.features.announcement.impl.store.AnnouncementStore
1213
import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore
1314
import io.element.android.tests.testutils.test
@@ -33,10 +34,10 @@ class AnnouncementPresenterTest {
3334
presenter.test {
3435
val state = awaitItem()
3536
assertThat(state.showSpaceAnnouncement).isFalse()
36-
store.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Show)
37+
store.setAnnouncementStatus(Announcement.Space, AnnouncementStore.AnnouncementStatus.Show)
3738
val updatedState = awaitItem()
3839
assertThat(updatedState.showSpaceAnnouncement).isTrue()
39-
store.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Shown)
40+
store.setAnnouncementStatus(Announcement.Space, AnnouncementStore.AnnouncementStatus.Shown)
4041
val finalState = awaitItem()
4142
assertThat(finalState.showSpaceAnnouncement).isFalse()
4243
}

features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ class DefaultAnnouncementServiceTest {
2525
val sut = createDefaultAnnouncementService(
2626
announcementStore = announcementStore,
2727
)
28-
assertThat(announcementStore.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.NeverShown)
28+
assertThat(announcementStore.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.NeverShown)
2929
sut.showAnnouncement(Announcement.Space)
30-
assertThat(announcementStore.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.Show)
30+
assertThat(announcementStore.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.Show)
3131
// Simulate user close the announcement
32-
announcementStore.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Shown)
32+
sut.onAnnouncementDismissed(Announcement.Space)
3333
// Entering again the space tab should not change the value
3434
sut.showAnnouncement(Announcement.Space)
35-
assertThat(announcementStore.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.Shown)
35+
assertThat(announcementStore.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.Shown)
3636
}
3737

3838
private fun createDefaultAnnouncementService(

features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenterTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package io.element.android.features.announcement.impl.spaces
99

1010
import com.google.common.truth.Truth.assertThat
11+
import io.element.android.features.announcement.api.Announcement
1112
import io.element.android.features.announcement.impl.store.AnnouncementStore
1213
import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore
1314
import io.element.android.tests.testutils.test
@@ -23,10 +24,10 @@ class SpaceAnnouncementPresenterTest {
2324
announcementStore = store,
2425
)
2526
presenter.test {
26-
assertThat(store.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.NeverShown)
27+
assertThat(store.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.NeverShown)
2728
val state = awaitItem()
2829
state.eventSink(SpaceAnnouncementEvents.Continue)
29-
assertThat(store.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.Shown)
30+
assertThat(store.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.Shown)
3031
}
3132
}
3233
}

0 commit comments

Comments
 (0)