Add app room state persistence and allow rooms to be marked with bubbles
This commit is contained in:
parent
6a1d360036
commit
d44c331eab
|
@ -180,6 +180,7 @@ internal class FeatureModules internal constructor(
|
||||||
chatEngineModule.engine,
|
chatEngineModule.engine,
|
||||||
context,
|
context,
|
||||||
storeModule.value.messageStore(),
|
storeModule.value.messageStore(),
|
||||||
|
storeModule.value.appRoomStore(),
|
||||||
deviceMeta,
|
deviceMeta,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package app.dapk.st.domain
|
||||||
import app.dapk.db.app.StDb
|
import app.dapk.db.app.StDb
|
||||||
import app.dapk.st.core.CoroutineDispatchers
|
import app.dapk.st.core.CoroutineDispatchers
|
||||||
import app.dapk.st.core.Preferences
|
import app.dapk.st.core.Preferences
|
||||||
|
import app.dapk.st.domain.application.AppRoomPersistence
|
||||||
import app.dapk.st.domain.application.eventlog.EventLogPersistence
|
import app.dapk.st.domain.application.eventlog.EventLogPersistence
|
||||||
import app.dapk.st.domain.application.eventlog.LoggingStore
|
import app.dapk.st.domain.application.eventlog.LoggingStore
|
||||||
import app.dapk.st.domain.application.message.MessageOptionsStore
|
import app.dapk.st.domain.application.message.MessageOptionsStore
|
||||||
|
@ -41,4 +42,6 @@ class StoreModule(
|
||||||
|
|
||||||
fun messageStore(): MessageOptionsStore = MessageOptionsStore(cachingPreferences)
|
fun messageStore(): MessageOptionsStore = MessageOptionsStore(cachingPreferences)
|
||||||
|
|
||||||
|
fun appRoomStore(): AppRoomPersistence = AppRoomPersistence(database, coroutineDispatchers)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package app.dapk.st.domain.application
|
||||||
|
|
||||||
|
import app.dapk.db.app.StDb
|
||||||
|
import app.dapk.st.core.CoroutineDispatchers
|
||||||
|
import app.dapk.st.core.withIoContext
|
||||||
|
import app.dapk.st.matrix.common.RoomId
|
||||||
|
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
||||||
|
import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
private val DEFAULT_STATE = AppRoomState(isChatBubble = false)
|
||||||
|
|
||||||
|
class AppRoomPersistence(
|
||||||
|
private val database: StDb,
|
||||||
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun observe(roomId: RoomId): Flow<AppRoomState> {
|
||||||
|
return database.appRoomQueries.select(roomId.value)
|
||||||
|
.asFlow()
|
||||||
|
.mapToOneOrNull(coroutineDispatchers.io)
|
||||||
|
.map { it?.let { AppRoomState(isChatBubble = it.is_bubble == 1) } ?: DEFAULT_STATE }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun markBubble(roomId: RoomId) = coroutineDispatchers.withIoContext {
|
||||||
|
database.appRoomQueries.updateBubble(roomId.value, is_bubble = 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun unmarkBubble(roomId: RoomId) = coroutineDispatchers.withIoContext {
|
||||||
|
database.appRoomQueries.updateBubble(roomId.value, is_bubble = 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class AppRoomState(
|
||||||
|
val isChatBubble: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS dbAppRoom (
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
is_bubble INTEGER AS Int NOT NULL,
|
||||||
|
PRIMARY KEY (room_id)
|
||||||
|
);
|
|
@ -1,18 +1,14 @@
|
||||||
CREATE TABLE dbAppRoom (
|
CREATE TABLE dbAppRoom (
|
||||||
room_id TEXT NOT NULL,
|
room_id TEXT NOT NULL,
|
||||||
is_bubble INTEGER AS Int NOT NULL DEFAULT 0,
|
is_bubble INTEGER AS Int NOT NULL,
|
||||||
PRIMARY KEY (room_id)
|
PRIMARY KEY (room_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
markBubble:
|
updateBubble:
|
||||||
INSERT OR REPLACE INTO dbAppRoom(room_id, is_bubble)
|
|
||||||
VALUES (?,?);
|
|
||||||
|
|
||||||
unmarkBubble:
|
|
||||||
INSERT OR REPLACE INTO dbAppRoom(room_id, is_bubble)
|
INSERT OR REPLACE INTO dbAppRoom(room_id, is_bubble)
|
||||||
VALUES (?,?);
|
VALUES (?,?);
|
||||||
|
|
||||||
select:
|
select:
|
||||||
SELECT is_bubble
|
SELECT *
|
||||||
FROM dbAppRoom
|
FROM dbAppRoom
|
||||||
WHERE room_id = ?;
|
WHERE room_id = ?;
|
|
@ -1,4 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS dbMutedRoom (
|
|
||||||
room_id TEXT NOT NULL,
|
|
||||||
PRIMARY KEY (room_id)
|
|
||||||
);
|
|
|
@ -1,5 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS dbStRoomMeta (
|
|
||||||
room_id TEXT NOT NULL,
|
|
||||||
is_bubble INTEGER AS Int NOT NULL DEFAULT 0,
|
|
||||||
PRIMARY KEY (room_id)
|
|
||||||
);
|
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
||||||
import app.dapk.st.core.DeviceMeta
|
import app.dapk.st.core.DeviceMeta
|
||||||
import app.dapk.st.core.JobBag
|
import app.dapk.st.core.JobBag
|
||||||
import app.dapk.st.core.ProvidableModule
|
import app.dapk.st.core.ProvidableModule
|
||||||
|
import app.dapk.st.domain.application.AppRoomPersistence
|
||||||
import app.dapk.st.state.createStateViewModel
|
import app.dapk.st.state.createStateViewModel
|
||||||
import app.dapk.st.domain.application.message.MessageOptionsStore
|
import app.dapk.st.domain.application.message.MessageOptionsStore
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
|
@ -16,6 +17,7 @@ class MessengerModule(
|
||||||
private val chatEngine: ChatEngine,
|
private val chatEngine: ChatEngine,
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val messageOptionsStore: MessageOptionsStore,
|
private val messageOptionsStore: MessageOptionsStore,
|
||||||
|
private val appRoomStore: AppRoomPersistence,
|
||||||
val deviceMeta: DeviceMeta,
|
val deviceMeta: DeviceMeta,
|
||||||
) : ProvidableModule {
|
) : ProvidableModule {
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ class MessengerModule(
|
||||||
messageOptionsStore,
|
messageOptionsStore,
|
||||||
RoomId(launchPayload.roomId),
|
RoomId(launchPayload.roomId),
|
||||||
launchPayload.attachments,
|
launchPayload.attachments,
|
||||||
|
appRoomStore,
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,13 +122,17 @@ internal fun MessengerScreen(
|
||||||
}
|
}
|
||||||
viewModel.dispatch(action)
|
viewModel.dispatch(action)
|
||||||
}
|
}
|
||||||
BooleanOption(value = it.isMuted, trueText = "Disable bubble", falseText = "Enable bubble") {
|
|
||||||
val action = when (it) {
|
state.appRoomState.takeIfContent()?.let {
|
||||||
true -> ChatBubble.Disable
|
BooleanOption(value = it.isChatBubble, trueText = "Disable bubble", falseText = "Enable bubble") {
|
||||||
false -> ChatBubble.Enable
|
val action = when (it) {
|
||||||
|
true -> ChatBubble.Disable
|
||||||
|
false -> ChatBubble.Enable
|
||||||
|
}
|
||||||
|
viewModel.dispatch(action)
|
||||||
}
|
}
|
||||||
viewModel.dispatch(action)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DropdownMenuItem(text = { Text("Leave room", color = MaterialTheme.colorScheme.onSecondaryContainer) }, onClick = {
|
DropdownMenuItem(text = { Text("Leave room", color = MaterialTheme.colorScheme.onSecondaryContainer) }, onClick = {
|
||||||
viewModel.dispatch(ScreenAction.LeaveRoom)
|
viewModel.dispatch(ScreenAction.LeaveRoom)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package app.dapk.st.messenger.state
|
package app.dapk.st.messenger.state
|
||||||
|
|
||||||
import app.dapk.st.design.components.BubbleModel
|
import app.dapk.st.design.components.BubbleModel
|
||||||
|
import app.dapk.st.domain.application.AppRoomState
|
||||||
import app.dapk.st.engine.MessengerPageState
|
import app.dapk.st.engine.MessengerPageState
|
||||||
import app.dapk.st.engine.RoomEvent
|
import app.dapk.st.engine.RoomEvent
|
||||||
import app.dapk.st.navigator.MessageAttachment
|
import app.dapk.st.navigator.MessageAttachment
|
||||||
|
@ -37,6 +38,7 @@ sealed interface ComponentLifecycle : Action {
|
||||||
|
|
||||||
sealed interface MessagesStateChange : Action {
|
sealed interface MessagesStateChange : Action {
|
||||||
data class Content(val content: MessengerPageState) : MessagesStateChange
|
data class Content(val content: MessengerPageState) : MessagesStateChange
|
||||||
|
data class AppRoomContent(val content: AppRoomState) : MessagesStateChange
|
||||||
data class MuteContent(val isMuted: Boolean) : MessagesStateChange
|
data class MuteContent(val isMuted: Boolean) : MessagesStateChange
|
||||||
data class ChatBubbleContent(val isChatBubble: Boolean) : MessagesStateChange
|
data class ChatBubbleContent(val isChatBubble: Boolean) : MessagesStateChange
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package app.dapk.st.messenger.state
|
package app.dapk.st.messenger.state
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import app.dapk.st.core.DeviceMeta
|
import app.dapk.st.core.DeviceMeta
|
||||||
import app.dapk.st.core.JobBag
|
import app.dapk.st.core.JobBag
|
||||||
import app.dapk.st.core.Lce
|
import app.dapk.st.core.Lce
|
||||||
import app.dapk.st.core.asString
|
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.AppRoomPersistence
|
||||||
import app.dapk.st.domain.application.message.MessageOptionsStore
|
import app.dapk.st.domain.application.message.MessageOptionsStore
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
import app.dapk.st.engine.MessengerPageState
|
import app.dapk.st.engine.MessengerPageState
|
||||||
|
@ -29,6 +31,7 @@ internal fun messengerReducer(
|
||||||
messageOptionsStore: MessageOptionsStore,
|
messageOptionsStore: MessageOptionsStore,
|
||||||
roomId: RoomId,
|
roomId: RoomId,
|
||||||
initialAttachments: List<MessageAttachment>?,
|
initialAttachments: List<MessageAttachment>?,
|
||||||
|
appRoomStore: AppRoomPersistence,
|
||||||
eventEmitter: suspend (MessengerEvent) -> Unit,
|
eventEmitter: suspend (MessengerEvent) -> Unit,
|
||||||
): ReducerFactory<MessengerScreenState> {
|
): ReducerFactory<MessengerScreenState> {
|
||||||
return createReducer(
|
return createReducer(
|
||||||
|
@ -45,6 +48,14 @@ internal fun messengerReducer(
|
||||||
val state = getState()
|
val state = getState()
|
||||||
when (action) {
|
when (action) {
|
||||||
is ComponentLifecycle.Visible -> {
|
is ComponentLifecycle.Visible -> {
|
||||||
|
jobBag.replace(
|
||||||
|
"appRoomState",
|
||||||
|
appRoomStore.observe(state.roomId)
|
||||||
|
.onEach { dispatch(MessagesStateChange.AppRoomContent(it)) }
|
||||||
|
.launchIn(coroutineScope)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
jobBag.replace(
|
jobBag.replace(
|
||||||
"messages", chatEngine.messages(state.roomId, disableReadReceipts = messageOptionsStore.isReadReceiptsDisabled())
|
"messages", chatEngine.messages(state.roomId, disableReadReceipts = messageOptionsStore.isReadReceiptsDisabled())
|
||||||
.onEach { dispatch(MessagesStateChange.Content(it)) }
|
.onEach { dispatch(MessagesStateChange.Content(it)) }
|
||||||
|
@ -52,10 +63,14 @@ internal fun messengerReducer(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ComponentLifecycle.Gone -> jobBag.cancel("messages")
|
ComponentLifecycle.Gone -> jobBag.cancelAll()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
change(MessagesStateChange.AppRoomContent::class) { action, state ->
|
||||||
|
state.copy(appRoomState = Lce.Content(action.content))
|
||||||
|
},
|
||||||
|
|
||||||
change(MessagesStateChange.Content::class) { action, state ->
|
change(MessagesStateChange.Content::class) { action, state ->
|
||||||
state.copy(roomState = Lce.Content(action.content))
|
state.copy(roomState = Lce.Content(action.content))
|
||||||
},
|
},
|
||||||
|
@ -174,9 +189,11 @@ internal fun messengerReducer(
|
||||||
},
|
},
|
||||||
|
|
||||||
async(ScreenAction.ChatBubble::class) { action ->
|
async(ScreenAction.ChatBubble::class) { action ->
|
||||||
|
Log.e("!!!", "$action")
|
||||||
|
val state = getState()
|
||||||
when (action) {
|
when (action) {
|
||||||
ScreenAction.ChatBubble.Disable -> {}
|
ScreenAction.ChatBubble.Disable -> appRoomStore.unmarkBubble(state.roomId)
|
||||||
ScreenAction.ChatBubble.Enable -> {}
|
ScreenAction.ChatBubble.Enable -> appRoomStore.markBubble(state.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
|
|
|
@ -2,6 +2,7 @@ package app.dapk.st.messenger.state
|
||||||
|
|
||||||
import app.dapk.st.core.Lce
|
import app.dapk.st.core.Lce
|
||||||
import app.dapk.st.design.components.BubbleModel
|
import app.dapk.st.design.components.BubbleModel
|
||||||
|
import app.dapk.st.domain.application.AppRoomState
|
||||||
import app.dapk.st.engine.MessengerPageState
|
import app.dapk.st.engine.MessengerPageState
|
||||||
import app.dapk.st.engine.RoomEvent
|
import app.dapk.st.engine.RoomEvent
|
||||||
import app.dapk.st.matrix.common.RoomId
|
import app.dapk.st.matrix.common.RoomId
|
||||||
|
@ -20,10 +21,6 @@ data class MessengerScreenState(
|
||||||
val appRoomState: Lce<AppRoomState>,
|
val appRoomState: Lce<AppRoomState>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class AppRoomState(
|
|
||||||
val isChatBubble: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ViewerState(
|
data class ViewerState(
|
||||||
val event: BubbleModel.Image,
|
val event: BubbleModel.Image,
|
||||||
)
|
)
|
||||||
|
|
|
@ -63,6 +63,7 @@ class MessengerReducerTest {
|
||||||
fakeMessageOptionsStore.instance,
|
fakeMessageOptionsStore.instance,
|
||||||
A_ROOM_ID,
|
A_ROOM_ID,
|
||||||
emptyList(),
|
emptyList(),
|
||||||
|
mockk(),
|
||||||
fakeEventSource,
|
fakeEventSource,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -405,6 +406,7 @@ class MessengerReducerTest {
|
||||||
fakeMessageOptionsStore.instance,
|
fakeMessageOptionsStore.instance,
|
||||||
A_ROOM_ID,
|
A_ROOM_ID,
|
||||||
initialAttachments = initialAttachments,
|
initialAttachments = initialAttachments,
|
||||||
|
appRoomStore = mockk(),
|
||||||
fakeEventSource,
|
fakeEventSource,
|
||||||
)
|
)
|
||||||
}(block)
|
}(block)
|
||||||
|
|
Loading…
Reference in New Issue