Improve AnnouncementService.
This commit is contained in:
parent
59ef782b3e
commit
752e846b1c
20 changed files with 169 additions and 81 deletions
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.collectAsState
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.announcement.api.Announcement
|
||||
import io.element.android.features.announcement.impl.store.AnnouncementStore
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
|
@ -23,8 +24,8 @@ class AnnouncementPresenter(
|
|||
@Composable
|
||||
override fun present(): AnnouncementState {
|
||||
val showSpaceAnnouncement by remember {
|
||||
announcementStore.spaceAnnouncementFlow().map {
|
||||
it == AnnouncementStore.SpaceAnnouncement.Show
|
||||
announcementStore.announcementStateFlow(Announcement.Space).map {
|
||||
it == AnnouncementStore.AnnouncementStatus.Show
|
||||
}
|
||||
}.collectAsState(false)
|
||||
return AnnouncementState(
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementSta
|
|||
import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementView
|
||||
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
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
|
|
@ -35,13 +37,36 @@ class DefaultAnnouncementService(
|
|||
override suspend fun showAnnouncement(announcement: Announcement) {
|
||||
when (announcement) {
|
||||
Announcement.Space -> showSpaceAnnouncement()
|
||||
Announcement.NewNotificationSound -> {
|
||||
announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStore.AnnouncementStatus.Show)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun onAnnouncementDismissed(announcement: Announcement) {
|
||||
announcementStore.setAnnouncementStatus(announcement, AnnouncementStore.AnnouncementStatus.Shown)
|
||||
}
|
||||
|
||||
override fun announcementsToShowFlow(): Flow<List<Announcement>> {
|
||||
return combine(
|
||||
announcementStore.announcementStateFlow(Announcement.Space),
|
||||
announcementStore.announcementStateFlow(Announcement.NewNotificationSound),
|
||||
) { spaceAnnouncementStatus, newNotificationSoundStatus ->
|
||||
buildList {
|
||||
if (spaceAnnouncementStatus == AnnouncementStore.AnnouncementStatus.Show) {
|
||||
add(Announcement.Space)
|
||||
}
|
||||
if (newNotificationSoundStatus == AnnouncementStore.AnnouncementStatus.Show) {
|
||||
add(Announcement.NewNotificationSound)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun showSpaceAnnouncement() {
|
||||
val currentValue = announcementStore.spaceAnnouncementFlow().first()
|
||||
if (currentValue == AnnouncementStore.SpaceAnnouncement.NeverShown) {
|
||||
announcementStore.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Show)
|
||||
val currentValue = announcementStore.announcementStateFlow(Announcement.Space).first()
|
||||
if (currentValue == AnnouncementStore.AnnouncementStatus.NeverShown) {
|
||||
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStore.AnnouncementStatus.Show)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ 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.AnnouncementStore
|
||||
import io.element.android.features.announcement.impl.store.AnnouncementStore.SpaceAnnouncement
|
||||
import io.element.android.features.announcement.impl.store.AnnouncementStore.AnnouncementStatus
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ class SpaceAnnouncementPresenter(
|
|||
fun handleEvents(event: SpaceAnnouncementEvents) {
|
||||
when (event) {
|
||||
SpaceAnnouncementEvents.Continue -> localCoroutineScope.launch {
|
||||
announcementStore.setSpaceAnnouncementValue(SpaceAnnouncement.Shown)
|
||||
announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,22 @@
|
|||
|
||||
package io.element.android.features.announcement.impl.store
|
||||
|
||||
import io.element.android.features.announcement.api.Announcement
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AnnouncementStore {
|
||||
suspend fun setSpaceAnnouncementValue(value: SpaceAnnouncement)
|
||||
fun spaceAnnouncementFlow(): Flow<SpaceAnnouncement>
|
||||
suspend fun setAnnouncementStatus(
|
||||
announcement: Announcement,
|
||||
status: AnnouncementStatus,
|
||||
)
|
||||
|
||||
fun announcementStateFlow(
|
||||
announcement: Announcement,
|
||||
): Flow<AnnouncementStatus>
|
||||
|
||||
suspend fun reset()
|
||||
|
||||
enum class SpaceAnnouncement {
|
||||
enum class AnnouncementStatus {
|
||||
NeverShown,
|
||||
Show,
|
||||
Shown,
|
||||
|
|
|
|||
|
|
@ -12,11 +12,13 @@ import androidx.datastore.preferences.core.intPreferencesKey
|
|||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.announcement.api.Announcement
|
||||
import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private val spaceAnnouncementKey = intPreferencesKey("spaceAnnouncement")
|
||||
private val showNewNotificationSoundBannerKey = intPreferencesKey("showNewNotificationSoundBanner")
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
@Inject
|
||||
|
|
@ -25,16 +27,23 @@ class DefaultAnnouncementStore(
|
|||
) : AnnouncementStore {
|
||||
private val store = preferenceDataStoreFactory.create("elementx_announcement")
|
||||
|
||||
override suspend fun setSpaceAnnouncementValue(value: AnnouncementStore.SpaceAnnouncement) {
|
||||
store.edit {
|
||||
it[spaceAnnouncementKey] = value.ordinal
|
||||
override suspend fun setAnnouncementStatus(announcement: Announcement, status: AnnouncementStore.AnnouncementStatus) {
|
||||
val key = announcement.toKey()
|
||||
store.edit { prefs ->
|
||||
prefs[key] = status.ordinal
|
||||
}
|
||||
}
|
||||
|
||||
override fun spaceAnnouncementFlow(): Flow<AnnouncementStore.SpaceAnnouncement> {
|
||||
override fun announcementStateFlow(announcement: Announcement): Flow<AnnouncementStore.AnnouncementStatus> {
|
||||
val key = announcement.toKey()
|
||||
// For NewNotificationSound, a migration will set it to Show on application upgrade (see AppMigration08)
|
||||
val defaultStatus = when (announcement) {
|
||||
Announcement.Space -> AnnouncementStore.AnnouncementStatus.NeverShown
|
||||
Announcement.NewNotificationSound -> AnnouncementStore.AnnouncementStatus.Shown
|
||||
}
|
||||
return store.data.map { prefs ->
|
||||
val ordinal = prefs[spaceAnnouncementKey] ?: AnnouncementStore.SpaceAnnouncement.NeverShown.ordinal
|
||||
AnnouncementStore.SpaceAnnouncement.entries.getOrElse(ordinal) { AnnouncementStore.SpaceAnnouncement.NeverShown }
|
||||
val ordinal = prefs[key] ?: defaultStatus.ordinal
|
||||
AnnouncementStore.AnnouncementStatus.entries.getOrElse(ordinal) { defaultStatus }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,3 +51,8 @@ class DefaultAnnouncementStore(
|
|||
store.edit { it.clear() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun Announcement.toKey() = when (this) {
|
||||
Announcement.Space -> spaceAnnouncementKey
|
||||
Announcement.NewNotificationSound -> showNewNotificationSoundBannerKey
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
package io.element.android.features.announcement.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.announcement.api.Announcement
|
||||
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
|
||||
|
|
@ -33,10 +34,10 @@ class AnnouncementPresenterTest {
|
|||
presenter.test {
|
||||
val state = awaitItem()
|
||||
assertThat(state.showSpaceAnnouncement).isFalse()
|
||||
store.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Show)
|
||||
store.setAnnouncementStatus(Announcement.Space, AnnouncementStore.AnnouncementStatus.Show)
|
||||
val updatedState = awaitItem()
|
||||
assertThat(updatedState.showSpaceAnnouncement).isTrue()
|
||||
store.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Shown)
|
||||
store.setAnnouncementStatus(Announcement.Space, AnnouncementStore.AnnouncementStatus.Shown)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.showSpaceAnnouncement).isFalse()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ class DefaultAnnouncementServiceTest {
|
|||
val sut = createDefaultAnnouncementService(
|
||||
announcementStore = announcementStore,
|
||||
)
|
||||
assertThat(announcementStore.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.NeverShown)
|
||||
assertThat(announcementStore.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.NeverShown)
|
||||
sut.showAnnouncement(Announcement.Space)
|
||||
assertThat(announcementStore.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.Show)
|
||||
assertThat(announcementStore.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.Show)
|
||||
// Simulate user close the announcement
|
||||
announcementStore.setSpaceAnnouncementValue(AnnouncementStore.SpaceAnnouncement.Shown)
|
||||
sut.onAnnouncementDismissed(Announcement.Space)
|
||||
// Entering again the space tab should not change the value
|
||||
sut.showAnnouncement(Announcement.Space)
|
||||
assertThat(announcementStore.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.Shown)
|
||||
assertThat(announcementStore.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.Shown)
|
||||
}
|
||||
|
||||
private fun createDefaultAnnouncementService(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
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.AnnouncementStore
|
||||
import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore
|
||||
import io.element.android.tests.testutils.test
|
||||
|
|
@ -23,10 +24,10 @@ class SpaceAnnouncementPresenterTest {
|
|||
announcementStore = store,
|
||||
)
|
||||
presenter.test {
|
||||
assertThat(store.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.NeverShown)
|
||||
assertThat(store.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.NeverShown)
|
||||
val state = awaitItem()
|
||||
state.eventSink(SpaceAnnouncementEvents.Continue)
|
||||
assertThat(store.spaceAnnouncementFlow().first()).isEqualTo(AnnouncementStore.SpaceAnnouncement.Shown)
|
||||
assertThat(store.announcementStateFlow(Announcement.Space).first()).isEqualTo(AnnouncementStore.AnnouncementStatus.Shown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,23 +7,33 @@
|
|||
|
||||
package io.element.android.features.announcement.impl.store
|
||||
|
||||
import io.element.android.features.announcement.api.Announcement
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class InMemoryAnnouncementStore(
|
||||
initialSpaceAnnouncement: AnnouncementStore.SpaceAnnouncement = AnnouncementStore.SpaceAnnouncement.NeverShown,
|
||||
initialSpaceAnnouncementStatus: AnnouncementStore.AnnouncementStatus = AnnouncementStore.AnnouncementStatus.NeverShown,
|
||||
initialNewNotificationSoundAnnouncementStatus: AnnouncementStore.AnnouncementStatus = AnnouncementStore.AnnouncementStatus.NeverShown,
|
||||
) : AnnouncementStore {
|
||||
private val spaceAnnouncement = MutableStateFlow(initialSpaceAnnouncement)
|
||||
override suspend fun setSpaceAnnouncementValue(value: AnnouncementStore.SpaceAnnouncement) {
|
||||
spaceAnnouncement.value = value
|
||||
private val spaceAnnouncement = MutableStateFlow(initialSpaceAnnouncementStatus)
|
||||
private val newNotificationSoundAnnouncement = MutableStateFlow(initialNewNotificationSoundAnnouncementStatus)
|
||||
|
||||
override suspend fun setAnnouncementStatus(announcement: Announcement, status: AnnouncementStore.AnnouncementStatus) {
|
||||
when (announcement) {
|
||||
Announcement.Space -> spaceAnnouncement.value = status
|
||||
Announcement.NewNotificationSound -> newNotificationSoundAnnouncement.value = status
|
||||
}
|
||||
}
|
||||
|
||||
override fun spaceAnnouncementFlow(): Flow<AnnouncementStore.SpaceAnnouncement> {
|
||||
return spaceAnnouncement.asStateFlow()
|
||||
override fun announcementStateFlow(announcement: Announcement): Flow<AnnouncementStore.AnnouncementStatus> {
|
||||
return when (announcement) {
|
||||
Announcement.Space -> spaceAnnouncement.asStateFlow()
|
||||
Announcement.NewNotificationSound -> newNotificationSoundAnnouncement.asStateFlow()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun reset() {
|
||||
spaceAnnouncement.value = AnnouncementStore.SpaceAnnouncement.NeverShown
|
||||
spaceAnnouncement.value = AnnouncementStore.AnnouncementStatus.NeverShown
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue