add room UI to mute room + persistence layer
This commit is contained in:
parent
e753cd30ad
commit
7e184cf200
|
@ -19,12 +19,24 @@ internal class SharedPreferencesDelegate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun store(key: String, value: Set<String>) {
|
||||||
|
coroutineDispatchers.withIoContext {
|
||||||
|
preferences.edit().putStringSet(key, value).apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun readString(key: String): String? {
|
override suspend fun readString(key: String): String? {
|
||||||
return coroutineDispatchers.withIoContext {
|
return coroutineDispatchers.withIoContext {
|
||||||
preferences.getString(key, null)
|
preferences.getString(key, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun readStrings(key: String): Set<String>? {
|
||||||
|
return coroutineDispatchers.withIoContext {
|
||||||
|
preferences.getStringSet(key, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun remove(key: String) {
|
override suspend fun remove(key: String) {
|
||||||
coroutineDispatchers.withIoContext {
|
coroutineDispatchers.withIoContext {
|
||||||
preferences.edit().remove(key).apply()
|
preferences.edit().remove(key).apply()
|
||||||
|
|
|
@ -18,6 +18,7 @@ import app.dapk.st.core.extensions.ErrorTracker
|
||||||
import app.dapk.st.core.extensions.unsafeLazy
|
import app.dapk.st.core.extensions.unsafeLazy
|
||||||
import app.dapk.st.directory.DirectoryModule
|
import app.dapk.st.directory.DirectoryModule
|
||||||
import app.dapk.st.domain.StoreModule
|
import app.dapk.st.domain.StoreModule
|
||||||
|
import app.dapk.st.domain.room.MutedRoomsStorePersistence
|
||||||
import app.dapk.st.engine.MatrixEngine
|
import app.dapk.st.engine.MatrixEngine
|
||||||
import app.dapk.st.firebase.messaging.MessagingModule
|
import app.dapk.st.firebase.messaging.MessagingModule
|
||||||
import app.dapk.st.home.BetaVersionUpgradeUseCase
|
import app.dapk.st.home.BetaVersionUpgradeUseCase
|
||||||
|
@ -163,6 +164,7 @@ internal class FeatureModules internal constructor(
|
||||||
context,
|
context,
|
||||||
storeModule.value.messageStore(),
|
storeModule.value.messageStore(),
|
||||||
deviceMeta,
|
deviceMeta,
|
||||||
|
MutedRoomsStorePersistence(storeModule.value.cachingPreferences)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val homeModule by unsafeLazy {
|
val homeModule by unsafeLazy {
|
||||||
|
|
|
@ -6,7 +6,8 @@ class JobBag {
|
||||||
|
|
||||||
private val jobs = mutableMapOf<String, Job>()
|
private val jobs = mutableMapOf<String, Job>()
|
||||||
|
|
||||||
fun add(key: String, job: Job) {
|
fun replace(key: String, job: Job) {
|
||||||
|
jobs[key]?.cancel()
|
||||||
jobs[key] = job
|
jobs[key] = job
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ package app.dapk.st.core
|
||||||
interface Preferences {
|
interface Preferences {
|
||||||
|
|
||||||
suspend fun store(key: String, value: String)
|
suspend fun store(key: String, value: String)
|
||||||
|
suspend fun store(key: String, value: Set<String>)
|
||||||
suspend fun readString(key: String): String?
|
suspend fun readString(key: String): String?
|
||||||
|
suspend fun readStrings(key: String): Set<String>?
|
||||||
suspend fun clear()
|
suspend fun clear()
|
||||||
suspend fun remove(key: String)
|
suspend fun remove(key: String)
|
||||||
}
|
}
|
||||||
|
@ -17,4 +19,14 @@ suspend fun CachedPreferences.readBoolean(key: String, defaultValue: Boolean) =
|
||||||
.toBooleanStrict()
|
.toBooleanStrict()
|
||||||
|
|
||||||
suspend fun Preferences.readBoolean(key: String) = this.readString(key)?.toBooleanStrict()
|
suspend fun Preferences.readBoolean(key: String) = this.readString(key)?.toBooleanStrict()
|
||||||
suspend fun Preferences.store(key: String, value: Boolean) = this.store(key, value.toString())
|
suspend fun Preferences.store(key: String, value: Boolean) = this.store(key, value.toString())
|
||||||
|
suspend fun Preferences.append(key: String, value: String) {
|
||||||
|
val current = this.readStrings(key) ?: emptySet()
|
||||||
|
this.store(key, current + value)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun Preferences.removeFromSet(key: String, value: String) {
|
||||||
|
val current = this.readStrings(key) ?: emptySet()
|
||||||
|
this.store(key, current - value)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,23 @@ import app.dapk.st.core.CachedPreferences
|
||||||
import app.dapk.st.core.Preferences
|
import app.dapk.st.core.Preferences
|
||||||
|
|
||||||
class CachingPreferences(private val cache: PropertyCache, private val preferences: Preferences) : CachedPreferences {
|
class CachingPreferences(private val cache: PropertyCache, private val preferences: Preferences) : CachedPreferences {
|
||||||
|
|
||||||
override suspend fun store(key: String, value: String) {
|
override suspend fun store(key: String, value: String) {
|
||||||
cache.setValue(key, value)
|
cache.setValue(key, value)
|
||||||
preferences.store(key, value)
|
preferences.store(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun store(key: String, value: Set<String>) {
|
||||||
|
cache.setValue(key, value)
|
||||||
|
preferences.store(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun readStrings(key: String): Set<String>? {
|
||||||
|
return cache.getValue(key) ?: preferences.readStrings(key)?.also {
|
||||||
|
cache.setValue(key, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun readString(key: String): String? {
|
override suspend fun readString(key: String): String? {
|
||||||
return cache.getValue(key) ?: preferences.readString(key)?.also {
|
return cache.getValue(key) ?: preferences.readString(key)?.also {
|
||||||
cache.setValue(key, it)
|
cache.setValue(key, it)
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package app.dapk.st.domain.room
|
||||||
|
|
||||||
|
import app.dapk.st.core.Preferences
|
||||||
|
import app.dapk.st.core.append
|
||||||
|
import app.dapk.st.core.removeFromSet
|
||||||
|
import app.dapk.st.matrix.common.RoomId
|
||||||
|
|
||||||
|
private const val KEY_MUTE = "mute"
|
||||||
|
|
||||||
|
interface MutedRoomsStore {
|
||||||
|
suspend fun mute(roomId: RoomId)
|
||||||
|
suspend fun unmute(roomId: RoomId)
|
||||||
|
suspend fun isMuted(roomId: RoomId): Boolean
|
||||||
|
suspend fun allMuted(): Set<RoomId>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MutedRoomsStorePersistence(
|
||||||
|
private val preferences: Preferences
|
||||||
|
) : MutedRoomsStore {
|
||||||
|
|
||||||
|
override suspend fun mute(roomId: RoomId) {
|
||||||
|
preferences.append(KEY_MUTE, roomId.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unmute(roomId: RoomId) {
|
||||||
|
preferences.removeFromSet(KEY_MUTE, roomId.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun isMuted(roomId: RoomId): Boolean {
|
||||||
|
val allMuted = allMuted()
|
||||||
|
println("??? isMuted - $roomId")
|
||||||
|
println("??? all - $allMuted")
|
||||||
|
return allMuted.contains(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun allMuted(): Set<RoomId> {
|
||||||
|
return preferences.readStrings(KEY_MUTE)?.map { RoomId(it) }?.toSet() ?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ internal fun directoryReducer(
|
||||||
multi(ComponentLifecycle::class) { action ->
|
multi(ComponentLifecycle::class) { action ->
|
||||||
when (action) {
|
when (action) {
|
||||||
ComponentLifecycle.OnVisible -> async { _ ->
|
ComponentLifecycle.OnVisible -> async { _ ->
|
||||||
jobBag.add(KEY_SYNCING_JOB, chatEngine.directory().onEach {
|
jobBag.replace(KEY_SYNCING_JOB, chatEngine.directory().onEach {
|
||||||
shortcutHandler.onDirectoryUpdate(it.map { it.overview })
|
shortcutHandler.onDirectoryUpdate(it.map { it.overview })
|
||||||
when (it.isEmpty()) {
|
when (it.isEmpty()) {
|
||||||
true -> dispatch(DirectoryStateChange.Empty)
|
true -> dispatch(DirectoryStateChange.Empty)
|
||||||
|
|
|
@ -38,7 +38,7 @@ class DirectoryReducerTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given directory content, when Visible, then updates shortcuts and dispatches room state`() = runReducerTest {
|
fun `given directory content, when Visible, then updates shortcuts and dispatches room state`() = runReducerTest {
|
||||||
fakeShortcutHandler.instance.expectUnit { it.onDirectoryUpdate(listOf(AN_OVERVIEW)) }
|
fakeShortcutHandler.instance.expectUnit { it.onDirectoryUpdate(listOf(AN_OVERVIEW)) }
|
||||||
fakeJobBag.instance.expect { it.add("sync", any()) }
|
fakeJobBag.instance.expect { it.replace("sync", any()) }
|
||||||
fakeChatEngine.givenDirectory().returns(flowOf(listOf(AN_OVERVIEW_STATE)))
|
fakeChatEngine.givenDirectory().returns(flowOf(listOf(AN_OVERVIEW_STATE)))
|
||||||
|
|
||||||
reduce(ComponentLifecycle.OnVisible)
|
reduce(ComponentLifecycle.OnVisible)
|
||||||
|
@ -49,7 +49,7 @@ class DirectoryReducerTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given no directory content, when Visible, then updates shortcuts and dispatches empty state`() = runReducerTest {
|
fun `given no directory content, when Visible, then updates shortcuts and dispatches empty state`() = runReducerTest {
|
||||||
fakeShortcutHandler.instance.expectUnit { it.onDirectoryUpdate(emptyList()) }
|
fakeShortcutHandler.instance.expectUnit { it.onDirectoryUpdate(emptyList()) }
|
||||||
fakeJobBag.instance.expect { it.add("sync", any()) }
|
fakeJobBag.instance.expect { it.replace("sync", any()) }
|
||||||
fakeChatEngine.givenDirectory().returns(flowOf(emptyList()))
|
fakeChatEngine.givenDirectory().returns(flowOf(emptyList()))
|
||||||
|
|
||||||
reduce(ComponentLifecycle.OnVisible)
|
reduce(ComponentLifecycle.OnVisible)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import app.dapk.st.domain.application.message.MessageOptionsStore
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
import app.dapk.st.matrix.common.RoomId
|
import app.dapk.st.matrix.common.RoomId
|
||||||
import app.dapk.st.messenger.state.MessengerState
|
import app.dapk.st.messenger.state.MessengerState
|
||||||
|
import app.dapk.st.domain.room.MutedRoomsStore
|
||||||
import app.dapk.st.messenger.state.messengerReducer
|
import app.dapk.st.messenger.state.messengerReducer
|
||||||
|
|
||||||
class MessengerModule(
|
class MessengerModule(
|
||||||
|
@ -17,6 +18,7 @@ class MessengerModule(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val messageOptionsStore: MessageOptionsStore,
|
private val messageOptionsStore: MessageOptionsStore,
|
||||||
private val deviceMeta: DeviceMeta,
|
private val deviceMeta: DeviceMeta,
|
||||||
|
private val mutedRoomsStore: MutedRoomsStore,
|
||||||
) : ProvidableModule {
|
) : ProvidableModule {
|
||||||
|
|
||||||
internal fun messengerState(launchPayload: MessagerActivityPayload): MessengerState {
|
internal fun messengerState(launchPayload: MessagerActivityPayload): MessengerState {
|
||||||
|
@ -27,6 +29,7 @@ class MessengerModule(
|
||||||
CopyToClipboard(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager),
|
CopyToClipboard(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager),
|
||||||
deviceMeta,
|
deviceMeta,
|
||||||
messageOptionsStore,
|
messageOptionsStore,
|
||||||
|
mutedRoomsStore,
|
||||||
RoomId(launchPayload.roomId),
|
RoomId(launchPayload.roomId),
|
||||||
launchPayload.attachments,
|
launchPayload.attachments,
|
||||||
it
|
it
|
||||||
|
|
|
@ -87,9 +87,17 @@ internal fun MessengerScreen(
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
Toolbar(onNavigate = { navigator.navigate.upToHome() }, roomTitle, actions = {
|
Toolbar(onNavigate = { navigator.navigate.upToHome() }, roomTitle, actions = {
|
||||||
// OverflowMenu {
|
OverflowMenu {
|
||||||
// DropdownMenuItem(text = { Text("Settings", color = MaterialTheme.colorScheme.onSecondaryContainer) }, onClick = {})
|
when (state.isMuted) {
|
||||||
// }
|
true -> DropdownMenuItem(text = { Text("Unmute notifications", color = MaterialTheme.colorScheme.onSecondaryContainer) }, onClick = {
|
||||||
|
viewModel.dispatch(ScreenAction.Notifications.Unmute)
|
||||||
|
})
|
||||||
|
|
||||||
|
false -> DropdownMenuItem(text = { Text("Mute notifications", color = MaterialTheme.colorScheme.onSecondaryContainer) }, onClick = {
|
||||||
|
viewModel.dispatch(ScreenAction.Notifications.Mute)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
when (state.composerState) {
|
when (state.composerState) {
|
||||||
|
|
|
@ -10,6 +10,11 @@ sealed interface ScreenAction : Action {
|
||||||
data class CopyToClipboard(val model: BubbleModel) : ScreenAction
|
data class CopyToClipboard(val model: BubbleModel) : ScreenAction
|
||||||
object SendMessage : ScreenAction
|
object SendMessage : ScreenAction
|
||||||
object OpenGalleryPicker : ScreenAction
|
object OpenGalleryPicker : ScreenAction
|
||||||
|
|
||||||
|
sealed interface Notifications : ScreenAction {
|
||||||
|
object Mute : Notifications
|
||||||
|
object Unmute : Notifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface ComponentLifecycle : Action {
|
sealed interface ComponentLifecycle : Action {
|
||||||
|
@ -18,7 +23,8 @@ sealed interface ComponentLifecycle : Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface MessagesStateChange : Action {
|
sealed interface MessagesStateChange : Action {
|
||||||
data class Content(val content: MessengerPageState) : ComposerStateChange
|
data class Content(val content: MessengerPageState) : MessagesStateChange
|
||||||
|
data class MuteContent(val isMuted: Boolean) : MessagesStateChange
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface ComposerStateChange : Action {
|
sealed interface ComposerStateChange : Action {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import app.dapk.st.core.asString
|
||||||
import app.dapk.st.core.extensions.takeIfContent
|
import app.dapk.st.core.extensions.takeIfContent
|
||||||
import app.dapk.st.design.components.BubbleModel
|
import app.dapk.st.design.components.BubbleModel
|
||||||
import app.dapk.st.domain.application.message.MessageOptionsStore
|
import app.dapk.st.domain.application.message.MessageOptionsStore
|
||||||
|
import app.dapk.st.domain.room.MutedRoomsStore
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
import app.dapk.st.engine.MessengerPageState
|
import app.dapk.st.engine.MessengerPageState
|
||||||
import app.dapk.st.engine.RoomEvent
|
import app.dapk.st.engine.RoomEvent
|
||||||
|
@ -26,6 +27,7 @@ internal fun messengerReducer(
|
||||||
copyToClipboard: CopyToClipboard,
|
copyToClipboard: CopyToClipboard,
|
||||||
deviceMeta: DeviceMeta,
|
deviceMeta: DeviceMeta,
|
||||||
messageOptionsStore: MessageOptionsStore,
|
messageOptionsStore: MessageOptionsStore,
|
||||||
|
mutedRoomsStore: MutedRoomsStore,
|
||||||
roomId: RoomId,
|
roomId: RoomId,
|
||||||
initialAttachments: List<MessageAttachment>?,
|
initialAttachments: List<MessageAttachment>?,
|
||||||
eventEmitter: suspend (MessengerEvent) -> Unit,
|
eventEmitter: suspend (MessengerEvent) -> Unit,
|
||||||
|
@ -36,16 +38,19 @@ internal fun messengerReducer(
|
||||||
roomState = Lce.Loading(),
|
roomState = Lce.Loading(),
|
||||||
composerState = initialComposerState(initialAttachments),
|
composerState = initialComposerState(initialAttachments),
|
||||||
viewerState = null,
|
viewerState = null,
|
||||||
|
isMuted = false,
|
||||||
),
|
),
|
||||||
|
|
||||||
async(ComponentLifecycle::class) { action ->
|
async(ComponentLifecycle::class) { action ->
|
||||||
val state = getState()
|
val state = getState()
|
||||||
when (action) {
|
when (action) {
|
||||||
is ComponentLifecycle.Visible -> {
|
is ComponentLifecycle.Visible -> {
|
||||||
jobBag.add("messages", chatEngine.messages(state.roomId, disableReadReceipts = messageOptionsStore.isReadReceiptsDisabled())
|
jobBag.replace(
|
||||||
.onEach { dispatch(MessagesStateChange.Content(it)) }
|
"messages", chatEngine.messages(state.roomId, disableReadReceipts = messageOptionsStore.isReadReceiptsDisabled())
|
||||||
.launchIn(coroutineScope)
|
.onEach { dispatch(MessagesStateChange.Content(it)) }
|
||||||
|
.launchIn(coroutineScope)
|
||||||
)
|
)
|
||||||
|
dispatch(MessagesStateChange.MuteContent(mutedRoomsStore.isMuted(roomId)))
|
||||||
}
|
}
|
||||||
|
|
||||||
ComponentLifecycle.Gone -> jobBag.cancel("messages")
|
ComponentLifecycle.Gone -> jobBag.cancel("messages")
|
||||||
|
@ -134,6 +139,28 @@ internal fun messengerReducer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
change(MessagesStateChange.MuteContent::class) { action, state ->
|
||||||
|
state.copy(isMuted = action.isMuted).also {
|
||||||
|
println("??? action - $action previous state: ${state.isMuted} next: ${it.isMuted}")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async(ScreenAction.Notifications::class) { action ->
|
||||||
|
when (action) {
|
||||||
|
ScreenAction.Notifications.Mute -> mutedRoomsStore.mute(roomId)
|
||||||
|
ScreenAction.Notifications.Unmute -> mutedRoomsStore.unmute(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
MessagesStateChange.MuteContent(
|
||||||
|
isMuted = when (action) {
|
||||||
|
ScreenAction.Notifications.Mute -> true
|
||||||
|
ScreenAction.Notifications.Unmute -> false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +185,6 @@ private fun RoomEvent.toSendMessageReply() = SendMessage.TextMessage.Reply(
|
||||||
timestampUtc = this.utcTimestamp,
|
timestampUtc = this.utcTimestamp,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
private fun initialComposerState(initialAttachments: List<MessageAttachment>?) = initialAttachments
|
private fun initialComposerState(initialAttachments: List<MessageAttachment>?) = initialAttachments
|
||||||
?.takeIf { it.isNotEmpty() }
|
?.takeIf { it.isNotEmpty() }
|
||||||
?.let { ComposerState.Attachments(it, null) }
|
?.let { ComposerState.Attachments(it, null) }
|
||||||
|
|
|
@ -14,7 +14,8 @@ data class MessengerScreenState(
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
val roomState: Lce<MessengerPageState>,
|
val roomState: Lce<MessengerPageState>,
|
||||||
val composerState: ComposerState,
|
val composerState: ComposerState,
|
||||||
val viewerState: ViewerState?
|
val viewerState: ViewerState?,
|
||||||
|
val isMuted: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ViewerState(
|
data class ViewerState(
|
||||||
|
|
|
@ -3,10 +3,12 @@ package app.dapk.st.messenger
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import app.dapk.st.core.*
|
import app.dapk.st.core.*
|
||||||
import app.dapk.st.design.components.BubbleModel
|
import app.dapk.st.design.components.BubbleModel
|
||||||
|
import app.dapk.st.domain.room.MutedRoomsStore
|
||||||
import app.dapk.st.engine.RoomEvent
|
import app.dapk.st.engine.RoomEvent
|
||||||
import app.dapk.st.engine.RoomState
|
import app.dapk.st.engine.RoomState
|
||||||
import app.dapk.st.engine.SendMessage
|
import app.dapk.st.engine.SendMessage
|
||||||
import app.dapk.st.matrix.common.EventId
|
import app.dapk.st.matrix.common.EventId
|
||||||
|
import app.dapk.st.matrix.common.RoomId
|
||||||
import app.dapk.st.matrix.common.UserId
|
import app.dapk.st.matrix.common.UserId
|
||||||
import app.dapk.st.matrix.common.asString
|
import app.dapk.st.matrix.common.asString
|
||||||
import app.dapk.st.messenger.state.*
|
import app.dapk.st.messenger.state.*
|
||||||
|
@ -14,6 +16,7 @@ import app.dapk.st.navigator.MessageAttachment
|
||||||
import fake.FakeChatEngine
|
import fake.FakeChatEngine
|
||||||
import fake.FakeMessageOptionsStore
|
import fake.FakeMessageOptionsStore
|
||||||
import fixture.*
|
import fixture.*
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
@ -27,6 +30,7 @@ private const val READ_RECEIPTS_ARE_DISABLED = true
|
||||||
private val A_ROOM_ID = aRoomId("messenger state room id")
|
private val A_ROOM_ID = aRoomId("messenger state room id")
|
||||||
private const val A_MESSAGE_CONTENT = "message content"
|
private const val A_MESSAGE_CONTENT = "message content"
|
||||||
private val AN_EVENT_ID = anEventId("state event")
|
private val AN_EVENT_ID = anEventId("state event")
|
||||||
|
private const val ROOM_IS_MUTED = true
|
||||||
private val A_SELF_ID = aUserId("self")
|
private val A_SELF_ID = aUserId("self")
|
||||||
private val A_MESSENGER_PAGE_STATE = aMessengerStateWithEvent(AN_EVENT_ID, A_SELF_ID)
|
private val A_MESSENGER_PAGE_STATE = aMessengerStateWithEvent(AN_EVENT_ID, A_SELF_ID)
|
||||||
private val A_MESSAGE_ATTACHMENT = MessageAttachment(AndroidUri("a-uri"), MimeType.Image)
|
private val A_MESSAGE_ATTACHMENT = MessageAttachment(AndroidUri("a-uri"), MimeType.Image)
|
||||||
|
@ -48,6 +52,7 @@ class MessengerReducerTest {
|
||||||
private val fakeChatEngine = FakeChatEngine()
|
private val fakeChatEngine = FakeChatEngine()
|
||||||
private val fakeCopyToClipboard = FakeCopyToClipboard()
|
private val fakeCopyToClipboard = FakeCopyToClipboard()
|
||||||
private val fakeDeviceMeta = FakeDeviceMeta()
|
private val fakeDeviceMeta = FakeDeviceMeta()
|
||||||
|
private val fakeMutedRoomsStore = FakeMutedRoomsStore()
|
||||||
private val fakeJobBag = FakeJobBag()
|
private val fakeJobBag = FakeJobBag()
|
||||||
|
|
||||||
private val runReducerTest = testReducer { fakeEventSource ->
|
private val runReducerTest = testReducer { fakeEventSource ->
|
||||||
|
@ -57,6 +62,7 @@ class MessengerReducerTest {
|
||||||
fakeCopyToClipboard.instance,
|
fakeCopyToClipboard.instance,
|
||||||
fakeDeviceMeta.instance,
|
fakeDeviceMeta.instance,
|
||||||
fakeMessageOptionsStore.instance,
|
fakeMessageOptionsStore.instance,
|
||||||
|
fakeMutedRoomsStore,
|
||||||
A_ROOM_ID,
|
A_ROOM_ID,
|
||||||
emptyList(),
|
emptyList(),
|
||||||
fakeEventSource,
|
fakeEventSource,
|
||||||
|
@ -71,6 +77,7 @@ class MessengerReducerTest {
|
||||||
roomState = Lce.Loading(),
|
roomState = Lce.Loading(),
|
||||||
composerState = ComposerState.Text(value = "", reply = null),
|
composerState = ComposerState.Text(value = "", reply = null),
|
||||||
viewerState = null,
|
viewerState = null,
|
||||||
|
isMuted = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -83,6 +90,7 @@ class MessengerReducerTest {
|
||||||
roomState = Lce.Loading(),
|
roomState = Lce.Loading(),
|
||||||
composerState = ComposerState.Text(value = "", reply = null),
|
composerState = ComposerState.Text(value = "", reply = null),
|
||||||
viewerState = null,
|
viewerState = null,
|
||||||
|
isMuted = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -95,20 +103,22 @@ class MessengerReducerTest {
|
||||||
roomState = Lce.Loading(),
|
roomState = Lce.Loading(),
|
||||||
composerState = ComposerState.Attachments(listOf(A_MESSAGE_ATTACHMENT), reply = null),
|
composerState = ComposerState.Attachments(listOf(A_MESSAGE_ATTACHMENT), reply = null),
|
||||||
viewerState = null,
|
viewerState = null,
|
||||||
|
isMuted = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given messages emits state, when Visible, then dispatches content`() = runReducerTest {
|
fun `given room is muted and messages emits state, when Visible, then dispatches content and mute changes`() = runReducerTest {
|
||||||
fakeJobBag.instance.expect { it.add("messages", any()) }
|
fakeMutedRoomsStore.givenIsMuted(A_ROOM_ID).returns(ROOM_IS_MUTED)
|
||||||
|
fakeJobBag.instance.expect { it.replace("messages", any()) }
|
||||||
fakeMessageOptionsStore.givenReadReceiptsDisabled().returns(READ_RECEIPTS_ARE_DISABLED)
|
fakeMessageOptionsStore.givenReadReceiptsDisabled().returns(READ_RECEIPTS_ARE_DISABLED)
|
||||||
val state = aMessengerStateWithEvent(AN_EVENT_ID, A_SELF_ID)
|
val state = aMessengerStateWithEvent(AN_EVENT_ID, A_SELF_ID)
|
||||||
fakeChatEngine.givenMessages(A_ROOM_ID, READ_RECEIPTS_ARE_DISABLED).returns(flowOf(state))
|
fakeChatEngine.givenMessages(A_ROOM_ID, READ_RECEIPTS_ARE_DISABLED).returns(flowOf(state))
|
||||||
|
|
||||||
reduce(ComponentLifecycle.Visible)
|
reduce(ComponentLifecycle.Visible)
|
||||||
|
|
||||||
assertOnlyDispatches(listOf(MessagesStateChange.Content(state)))
|
assertOnlyDispatches(listOf(MessagesStateChange.MuteContent(isMuted = ROOM_IS_MUTED), MessagesStateChange.Content(state)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -330,6 +340,7 @@ class MessengerReducerTest {
|
||||||
fakeCopyToClipboard.instance,
|
fakeCopyToClipboard.instance,
|
||||||
fakeDeviceMeta.instance,
|
fakeDeviceMeta.instance,
|
||||||
fakeMessageOptionsStore.instance,
|
fakeMessageOptionsStore.instance,
|
||||||
|
FakeMutedRoomsStore(),
|
||||||
A_ROOM_ID,
|
A_ROOM_ID,
|
||||||
initialAttachments = initialAttachments,
|
initialAttachments = initialAttachments,
|
||||||
fakeEventSource,
|
fakeEventSource,
|
||||||
|
@ -359,4 +370,8 @@ class FakeDeviceMeta {
|
||||||
val instance = mockk<DeviceMeta>()
|
val instance = mockk<DeviceMeta>()
|
||||||
|
|
||||||
fun givenApiVersion() = every { instance.apiVersion }.delegateReturn()
|
fun givenApiVersion() = every { instance.apiVersion }.delegateReturn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeMutedRoomsStore : MutedRoomsStore by mockk() {
|
||||||
|
fun givenIsMuted(roomId: RoomId) = coEvery { isMuted(roomId) }.delegateReturn()
|
||||||
|
}
|
||||||
|
|
|
@ -1,18 +1,31 @@
|
||||||
package test.impl
|
package test.impl
|
||||||
|
|
||||||
import app.dapk.st.core.Preferences
|
import app.dapk.st.core.Preferences
|
||||||
import test.unit
|
|
||||||
|
|
||||||
class InMemoryPreferences : Preferences {
|
class InMemoryPreferences : Preferences {
|
||||||
|
|
||||||
private val prefs = mutableMapOf<String, String>()
|
private val prefs = mutableMapOf<String, String>()
|
||||||
|
private val setPrefs = mutableMapOf<String, Set<String>>()
|
||||||
|
|
||||||
override suspend fun store(key: String, value: String) {
|
override suspend fun store(key: String, value: String) {
|
||||||
prefs[key] = value
|
prefs[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun store(key: String, value: Set<String>) {
|
||||||
|
setPrefs[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun readString(key: String): String? = prefs[key]
|
override suspend fun readString(key: String): String? = prefs[key]
|
||||||
override suspend fun remove(key: String) = prefs.remove(key).unit()
|
override suspend fun readStrings(key: String): Set<String>? = setPrefs[key]
|
||||||
override suspend fun clear() = prefs.clear()
|
|
||||||
|
override suspend fun remove(key: String) {
|
||||||
|
prefs.remove(key)
|
||||||
|
setPrefs.remove(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clear() {
|
||||||
|
prefs.clear()
|
||||||
|
setPrefs.clear()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue