Add app room state persistence and allow rooms to be marked with bubbles

This commit is contained in:
Adam Brown 2023-01-26 21:30:46 +00:00
parent 6a1d360036
commit d44c331eab
14 changed files with 87 additions and 28 deletions

View File

@ -180,6 +180,7 @@ internal class FeatureModules internal constructor(
chatEngineModule.engine,
context,
storeModule.value.messageStore(),
storeModule.value.appRoomStore(),
deviceMeta,
)
}

View File

@ -3,6 +3,7 @@ package app.dapk.st.domain
import app.dapk.db.app.StDb
import app.dapk.st.core.CoroutineDispatchers
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.LoggingStore
import app.dapk.st.domain.application.message.MessageOptionsStore
@ -41,4 +42,6 @@ class StoreModule(
fun messageStore(): MessageOptionsStore = MessageOptionsStore(cachingPreferences)
fun appRoomStore(): AppRoomPersistence = AppRoomPersistence(database, coroutineDispatchers)
}

View File

@ -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
)

View File

@ -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)
);

View File

@ -1,18 +1,14 @@
CREATE TABLE dbAppRoom (
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)
);
markBubble:
INSERT OR REPLACE INTO dbAppRoom(room_id, is_bubble)
VALUES (?,?);
unmarkBubble:
updateBubble:
INSERT OR REPLACE INTO dbAppRoom(room_id, is_bubble)
VALUES (?,?);
select:
SELECT is_bubble
SELECT *
FROM dbAppRoom
WHERE room_id = ?;

View File

@ -1,4 +0,0 @@
CREATE TABLE IF NOT EXISTS dbMutedRoom (
room_id TEXT NOT NULL,
PRIMARY KEY (room_id)
);

View File

@ -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)
);

View File

@ -5,6 +5,7 @@ import android.content.Context
import app.dapk.st.core.DeviceMeta
import app.dapk.st.core.JobBag
import app.dapk.st.core.ProvidableModule
import app.dapk.st.domain.application.AppRoomPersistence
import app.dapk.st.state.createStateViewModel
import app.dapk.st.domain.application.message.MessageOptionsStore
import app.dapk.st.engine.ChatEngine
@ -16,6 +17,7 @@ class MessengerModule(
private val chatEngine: ChatEngine,
private val context: Context,
private val messageOptionsStore: MessageOptionsStore,
private val appRoomStore: AppRoomPersistence,
val deviceMeta: DeviceMeta,
) : ProvidableModule {
@ -29,6 +31,7 @@ class MessengerModule(
messageOptionsStore,
RoomId(launchPayload.roomId),
launchPayload.attachments,
appRoomStore,
it
)
}

View File

@ -122,13 +122,17 @@ internal fun MessengerScreen(
}
viewModel.dispatch(action)
}
BooleanOption(value = it.isMuted, trueText = "Disable bubble", falseText = "Enable bubble") {
val action = when (it) {
true -> ChatBubble.Disable
false -> ChatBubble.Enable
state.appRoomState.takeIfContent()?.let {
BooleanOption(value = it.isChatBubble, trueText = "Disable bubble", falseText = "Enable bubble") {
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 = {
viewModel.dispatch(ScreenAction.LeaveRoom)
})

View File

@ -1,6 +1,7 @@
package app.dapk.st.messenger.state
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.RoomEvent
import app.dapk.st.navigator.MessageAttachment
@ -37,6 +38,7 @@ sealed interface ComponentLifecycle : Action {
sealed interface MessagesStateChange : Action {
data class Content(val content: MessengerPageState) : MessagesStateChange
data class AppRoomContent(val content: AppRoomState) : MessagesStateChange
data class MuteContent(val isMuted: Boolean) : MessagesStateChange
data class ChatBubbleContent(val isChatBubble: Boolean) : MessagesStateChange
}

View File

@ -1,12 +1,14 @@
package app.dapk.st.messenger.state
import android.os.Build
import android.util.Log
import app.dapk.st.core.DeviceMeta
import app.dapk.st.core.JobBag
import app.dapk.st.core.Lce
import app.dapk.st.core.asString
import app.dapk.st.core.extensions.takeIfContent
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.engine.ChatEngine
import app.dapk.st.engine.MessengerPageState
@ -29,6 +31,7 @@ internal fun messengerReducer(
messageOptionsStore: MessageOptionsStore,
roomId: RoomId,
initialAttachments: List<MessageAttachment>?,
appRoomStore: AppRoomPersistence,
eventEmitter: suspend (MessengerEvent) -> Unit,
): ReducerFactory<MessengerScreenState> {
return createReducer(
@ -45,6 +48,14 @@ internal fun messengerReducer(
val state = getState()
when (action) {
is ComponentLifecycle.Visible -> {
jobBag.replace(
"appRoomState",
appRoomStore.observe(state.roomId)
.onEach { dispatch(MessagesStateChange.AppRoomContent(it)) }
.launchIn(coroutineScope)
)
jobBag.replace(
"messages", chatEngine.messages(state.roomId, disableReadReceipts = messageOptionsStore.isReadReceiptsDisabled())
.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 ->
state.copy(roomState = Lce.Content(action.content))
},
@ -174,9 +189,11 @@ internal fun messengerReducer(
},
async(ScreenAction.ChatBubble::class) { action ->
Log.e("!!!", "$action")
val state = getState()
when (action) {
ScreenAction.ChatBubble.Disable -> {}
ScreenAction.ChatBubble.Enable -> {}
ScreenAction.ChatBubble.Disable -> appRoomStore.unmarkBubble(state.roomId)
ScreenAction.ChatBubble.Enable -> appRoomStore.markBubble(state.roomId)
}
dispatch(

View File

@ -2,6 +2,7 @@ package app.dapk.st.messenger.state
import app.dapk.st.core.Lce
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.RoomEvent
import app.dapk.st.matrix.common.RoomId
@ -20,10 +21,6 @@ data class MessengerScreenState(
val appRoomState: Lce<AppRoomState>,
)
data class AppRoomState(
val isChatBubble: Boolean
)
data class ViewerState(
val event: BubbleModel.Image,
)

View File

@ -63,6 +63,7 @@ class MessengerReducerTest {
fakeMessageOptionsStore.instance,
A_ROOM_ID,
emptyList(),
mockk(),
fakeEventSource,
)
}
@ -405,6 +406,7 @@ class MessengerReducerTest {
fakeMessageOptionsStore.instance,
A_ROOM_ID,
initialAttachments = initialAttachments,
appRoomStore = mockk(),
fakeEventSource,
)
}(block)