Skip to content

Commit

Permalink
Merge pull request #243 from ouchadam/feature/mute-room-notifications
Browse files Browse the repository at this point in the history
Mute room notifications
  • Loading branch information
ouchadam authored Nov 2, 2022
2 parents 72fa795 + 87083b0 commit 1237cd2
Show file tree
Hide file tree
Showing 37 changed files with 592 additions and 324 deletions.
2 changes: 2 additions & 0 deletions chat-engine/src/main/kotlin/app/dapk/st/engine/ChatEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ interface ChatEngine : TaskRunner {

fun pushHandler(): PushHandler

suspend fun muteRoom(roomId: RoomId)
suspend fun unmuteRoom(roomId: RoomId)
}

interface TaskRunner {
Expand Down
6 changes: 4 additions & 2 deletions chat-engine/src/main/kotlin/app/dapk/st/engine/Models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ typealias InviteState = List<RoomInvite>
data class DirectoryItem(
val overview: RoomOverview,
val unreadCount: UnreadCount,
val typing: Typing?
val typing: Typing?,
val isMuted: Boolean,
)

data class RoomOverview(
Expand Down Expand Up @@ -87,7 +88,8 @@ sealed interface ImportResult {
data class MessengerPageState(
val self: UserId,
val roomState: RoomState,
val typing: Typing?
val typing: Typing?,
val isMuted: Boolean,
)

data class RoomState(
Expand Down
5 changes: 3 additions & 2 deletions chat-engine/src/testFixtures/kotlin/fixture/Fixtures.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import app.dapk.st.matrix.common.*
fun aMessengerState(
self: UserId = aUserId(),
roomState: RoomState,
typing: Typing? = null
) = MessengerPageState(self, roomState, typing)
typing: Typing? = null,
isMuted: Boolean = false,
) = MessengerPageState(self, roomState, typing, isMuted)

fun aRoomOverview(
roomId: RoomId = aRoomId(),
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/kotlin/app/dapk/st/core/JobBag.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class JobBag {

private val jobs = mutableMapOf<String, Job>()

fun add(key: String, job: Job) {
fun replace(key: String, job: Job) {
jobs[key]?.cancel()
jobs[key] = job
}

Expand Down
3 changes: 2 additions & 1 deletion core/src/main/kotlin/app/dapk/st/core/Preferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ suspend fun CachedPreferences.readBoolean(key: String, defaultValue: Boolean) =
.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())

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import app.dapk.state.Store
import app.dapk.state.createStore
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch

class StateViewModel<S, E>(
reducerFactory: ReducerFactory<S>,
Expand All @@ -32,7 +31,7 @@ class StateViewModel<S, E>(
}

override fun dispatch(action: Action) {
viewModelScope.launch { store.dispatch(action) }
store.dispatch(action)
}
}

Expand Down
16 changes: 10 additions & 6 deletions domains/state/src/main/kotlin/app/dapk/state/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ fun <S> createStore(reducerFactory: ReducerFactory<S>, coroutineScope: Coroutine
private val scope = createScope(coroutineScope, this)
private val reducer = reducerFactory.create(scope)

override suspend fun dispatch(action: Action) {
scope.coroutineScope.launch {
override fun dispatch(action: Action) {
coroutineScope.launch {
state = reducer.reduce(action).also { nextState ->
if (nextState != state) {
subscribers.forEach { it.invoke(nextState) }
Expand All @@ -35,7 +35,7 @@ interface ReducerFactory<S> {
}

fun interface Reducer<S> {
suspend fun reduce(action: Action): S
fun reduce(action: Action): S
}

private fun <S> createScope(coroutineScope: CoroutineScope, store: Store<S>) = object : ReducerScope<S> {
Expand All @@ -45,7 +45,7 @@ private fun <S> createScope(coroutineScope: CoroutineScope, store: Store<S>) = o
}

interface Store<S> {
suspend fun dispatch(action: Action)
fun dispatch(action: Action)
fun getState(): S
fun subscribe(subscriber: (S) -> Unit)
}
Expand Down Expand Up @@ -82,14 +82,18 @@ fun <S> createReducer(
actionHandlers.fold(acc) { acc, handler ->
when (handler) {
is ActionHandler.Async -> {
handler.handler.invoke(scope, action)
scope.coroutineScope.launch {
handler.handler.invoke(scope, action)
}
acc
}

is ActionHandler.Sync -> handler.handler.invoke(action, acc)
is ActionHandler.Delegate -> when (val next = handler.handler.invoke(scope, action)) {
is ActionHandler.Async -> {
next.handler.invoke(scope, action)
scope.coroutineScope.launch {
next.handler.invoke(scope, action)
}
acc
}

Expand Down
2 changes: 1 addition & 1 deletion domains/state/src/testFixtures/kotlin/test/ReducerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class ReducerTestScope<S, E>(
}
private val reducer: Reducer<S> = reducerFactory.create(reducerScope)

override suspend fun reduce(action: Action) = reducer.reduce(action).also {
override fun reduce(action: Action) = reducer.reduce(action).also {
capturedResult = it
}

Expand Down
13 changes: 12 additions & 1 deletion domains/store/src/main/kotlin/app/dapk/st/domain/StoreModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import app.dapk.st.domain.preference.CachingPreferences
import app.dapk.st.domain.preference.PropertyCache
import app.dapk.st.domain.profile.ProfilePersistence
import app.dapk.st.domain.push.PushTokenRegistrarPreferences
import app.dapk.st.domain.room.MutedStorePersistence
import app.dapk.st.domain.sync.OverviewPersistence
import app.dapk.st.domain.sync.RoomPersistence
import app.dapk.st.matrix.common.CredentialsStore
Expand All @@ -33,8 +34,18 @@ class StoreModule(
private val coroutineDispatchers: CoroutineDispatchers,
) {

private val muteableStore by unsafeLazy { MutedStorePersistence(database, coroutineDispatchers) }

fun overviewStore(): OverviewStore = OverviewPersistence(database, coroutineDispatchers)
fun roomStore(): RoomStore = RoomPersistence(database, OverviewPersistence(database, coroutineDispatchers), coroutineDispatchers)
fun roomStore(): RoomStore {
return RoomPersistence(
database = database,
overviewPersistence = OverviewPersistence(database, coroutineDispatchers),
coroutineDispatchers = coroutineDispatchers,
muteableStore = muteableStore,
)
}

fun credentialsStore(): CredentialsStore = CredentialsPreferences(credentialPreferences)
fun syncStore(): SyncStore = SyncTokenPreferences(preferences)
fun filterStore(): FilterStore = FilterPreferences(preferences)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import app.dapk.st.core.CachedPreferences
import app.dapk.st.core.Preferences

class CachingPreferences(private val cache: PropertyCache, private val preferences: Preferences) : CachedPreferences {

override suspend fun store(key: String, value: String) {
cache.setValue(key, value)
preferences.store(key, value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package app.dapk.st.domain.room

import app.dapk.db.DapkDb
import app.dapk.st.core.CoroutineDispatchers
import app.dapk.st.core.withIoContext
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.sync.MuteableStore
import com.squareup.sqldelight.runtime.coroutines.asFlow
import com.squareup.sqldelight.runtime.coroutines.mapToList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map

internal class MutedStorePersistence(
private val database: DapkDb,
private val coroutineDispatchers: CoroutineDispatchers,
) : MuteableStore {

private val allMutedFlow = MutableSharedFlow<Set<RoomId>>(replay = 1)

override suspend fun mute(roomId: RoomId) {
coroutineDispatchers.withIoContext {
database.mutedRoomQueries.insertMuted(roomId.value)
}
}

override suspend fun unmute(roomId: RoomId) {
coroutineDispatchers.withIoContext {
database.mutedRoomQueries.removeMuted(roomId.value)
}
}

override suspend fun isMuted(roomId: RoomId) = allMutedFlow.firstOrNull()?.contains(roomId) ?: false

override fun observeMuted(): Flow<Set<RoomId>> = database.mutedRoomQueries.select()
.asFlow()
.mapToList()
.map { it.map { RoomId(it) }.toSet() }

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import app.dapk.db.DapkDb
import app.dapk.db.model.RoomEventQueries
import app.dapk.st.core.CoroutineDispatchers
import app.dapk.st.core.withIoContext
import app.dapk.st.domain.room.MutedStorePersistence
import app.dapk.st.matrix.common.EventId
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.sync.RoomEvent
import app.dapk.st.matrix.sync.RoomOverview
import app.dapk.st.matrix.sync.RoomState
import app.dapk.st.matrix.sync.RoomStore
import app.dapk.st.matrix.sync.*
import com.squareup.sqldelight.Query
import com.squareup.sqldelight.runtime.coroutines.asFlow
import com.squareup.sqldelight.runtime.coroutines.mapToList
import com.squareup.sqldelight.runtime.coroutines.mapToOneNotNull
Expand All @@ -25,7 +24,8 @@ internal class RoomPersistence(
private val database: DapkDb,
private val overviewPersistence: OverviewPersistence,
private val coroutineDispatchers: CoroutineDispatchers,
) : RoomStore {
private val muteableStore: MutedStorePersistence,
) : RoomStore, MuteableStore by muteableStore {

override suspend fun persist(roomId: RoomId, events: List<RoomEvent>) {
coroutineDispatchers.withIoContext {
Expand Down Expand Up @@ -57,10 +57,8 @@ internal class RoomPersistence(
}.distinctUntilChanged()

return database.roomEventQueries.selectRoom(roomId.value)
.asFlow()
.mapToList()
.distinctFlowList()
.map { it.map { json.decodeFromString(RoomEvent.serializer(), it) } }
.distinctUntilChanged()
.combine(overviewFlow) { events, overview ->
RoomState(overview, events)
}
Expand Down Expand Up @@ -92,9 +90,7 @@ internal class RoomPersistence(

override fun observeUnread(): Flow<Map<RoomOverview, List<RoomEvent>>> {
return database.roomEventQueries.selectAllUnread()
.asFlow()
.mapToList()
.distinctUntilChanged()
.distinctFlowList()
.map {
it.groupBy { RoomId(it.room_id) }
.mapKeys { overviewPersistence.retrieve(it.key)!! }
Expand All @@ -116,6 +112,22 @@ internal class RoomPersistence(
}
}

override fun observeNotMutedUnread(): Flow<Map<RoomOverview, List<RoomEvent>>> {
return database.roomEventQueries.selectNotMutedUnread()
.distinctFlowList()
.map {
it.groupBy { RoomId(it.room_id) }
.mapKeys { overviewPersistence.retrieve(it.key)!! }
.mapValues {
it.value.map {
json.decodeFromString(RoomEvent.serializer(), it.blob)
}
}
}
}

private fun <T : Any> Query<T>.distinctFlowList() = this.asFlow().mapToList().distinctUntilChanged()

override suspend fun markRead(roomId: RoomId) {
coroutineDispatchers.withIoContext {
database.unreadEventQueries.removeRead(room_id = roomId.value)
Expand Down
16 changes: 16 additions & 0 deletions domains/store/src/main/sqldelight/app/dapk/db/model/MutedRoom.sq
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS dbMutedRoom (
room_id TEXT NOT NULL,
PRIMARY KEY (room_id)
);

insertMuted:
INSERT OR REPLACE INTO dbMutedRoom(room_id)
VALUES (?);

removeMuted:
DELETE FROM dbMutedRoom
WHERE room_id = ?;

select:
SELECT room_id
FROM dbMutedRoom;
10 changes: 10 additions & 0 deletions domains/store/src/main/sqldelight/app/dapk/db/model/RoomEvent.sq
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ INNER JOIN dbRoomEvent ON dbUnreadEvent.event_id = dbRoomEvent.event_id
ORDER BY dbRoomEvent.timestamp_utc DESC
LIMIT 100;

selectNotMutedUnread:
SELECT dbRoomEvent.blob, dbRoomEvent.room_id
FROM dbUnreadEvent
INNER JOIN dbRoomEvent ON dbUnreadEvent.event_id = dbRoomEvent.event_id
LEFT OUTER JOIN dbMutedRoom
ON dbUnreadEvent.room_id = dbMutedRoom.room_id
WHERE dbMutedRoom.room_id IS NULL
ORDER BY dbRoomEvent.timestamp_utc DESC
LIMIT 100;

remove:
DELETE FROM dbRoomEvent
WHERE room_id = ?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ FROM dbRoomMember
WHERE room_id = ?
LIMIT ?;


insert:
INSERT OR REPLACE INTO dbRoomMember(user_id, room_id, blob)
VALUES (?, ?, ?);
Loading

0 comments on commit 1237cd2

Please sign in to comment.