lifts room muting to the chat engine

This commit is contained in:
Adam Brown 2022-11-02 11:48:59 +00:00
parent 7e184cf200
commit dc5a46e0cd
16 changed files with 342 additions and 279 deletions

View File

@ -18,7 +18,6 @@ import app.dapk.st.core.extensions.ErrorTracker
import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.directory.DirectoryModule
import app.dapk.st.domain.StoreModule
import app.dapk.st.domain.room.MutedRoomsStorePersistence
import app.dapk.st.engine.MatrixEngine
import app.dapk.st.firebase.messaging.MessagingModule
import app.dapk.st.home.BetaVersionUpgradeUseCase
@ -164,7 +163,6 @@ internal class FeatureModules internal constructor(
context,
storeModule.value.messageStore(),
deviceMeta,
MutedRoomsStorePersistence(storeModule.value.cachingPreferences)
)
}
val homeModule by unsafeLazy {

View File

@ -36,6 +36,8 @@ interface ChatEngine : TaskRunner {
fun pushHandler(): PushHandler
suspend fun muteRoom(roomId: RoomId)
suspend fun unmuteRoom(roomId: RoomId)
}
interface TaskRunner {

View File

@ -87,7 +87,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(

View File

@ -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
@ -34,7 +35,13 @@ class StoreModule(
) {
fun overviewStore(): OverviewStore = OverviewPersistence(database, coroutineDispatchers)
fun roomStore(): RoomStore = RoomPersistence(database, OverviewPersistence(database, coroutineDispatchers), coroutineDispatchers)
fun roomStore(): RoomStore = RoomPersistence(
database = database,
overviewPersistence = OverviewPersistence(database, coroutineDispatchers),
coroutineDispatchers = coroutineDispatchers,
muteableStore = MutedStorePersistence(preferences),
)
fun credentialsStore(): CredentialsStore = CredentialsPreferences(credentialPreferences)
fun syncStore(): SyncStore = SyncTokenPreferences(preferences)
fun filterStore(): FilterStore = FilterPreferences(preferences)

View File

@ -4,19 +4,13 @@ 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
import app.dapk.st.matrix.sync.MuteableStore
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(
internal class MutedStorePersistence(
private val preferences: Preferences
) : MutedRoomsStore {
) : MuteableStore {
override suspend fun mute(roomId: RoomId) {
preferences.append(KEY_MUTE, roomId.value)
@ -28,8 +22,6 @@ class MutedRoomsStorePersistence(
override suspend fun isMuted(roomId: RoomId): Boolean {
val allMuted = allMuted()
println("??? isMuted - $roomId")
println("??? all - $allMuted")
return allMuted.contains(roomId)
}

View File

@ -4,12 +4,10 @@ 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.runtime.coroutines.asFlow
import com.squareup.sqldelight.runtime.coroutines.mapToList
import com.squareup.sqldelight.runtime.coroutines.mapToOneNotNull
@ -25,7 +23,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 {

View File

@ -10,7 +10,6 @@ import app.dapk.st.domain.application.message.MessageOptionsStore
import app.dapk.st.engine.ChatEngine
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.messenger.state.MessengerState
import app.dapk.st.domain.room.MutedRoomsStore
import app.dapk.st.messenger.state.messengerReducer
class MessengerModule(
@ -18,7 +17,6 @@ class MessengerModule(
private val context: Context,
private val messageOptionsStore: MessageOptionsStore,
private val deviceMeta: DeviceMeta,
private val mutedRoomsStore: MutedRoomsStore,
) : ProvidableModule {
internal fun messengerState(launchPayload: MessagerActivityPayload): MessengerState {
@ -29,7 +27,6 @@ class MessengerModule(
CopyToClipboard(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager),
deviceMeta,
messageOptionsStore,
mutedRoomsStore,
RoomId(launchPayload.roomId),
launchPayload.attachments,
it

View File

@ -87,8 +87,9 @@ internal fun MessengerScreen(
Column {
Toolbar(onNavigate = { navigator.navigate.upToHome() }, roomTitle, actions = {
state.roomState.takeIfContent()?.let {
OverflowMenu {
when (state.isMuted) {
when (it.isMuted) {
true -> DropdownMenuItem(text = { Text("Unmute notifications", color = MaterialTheme.colorScheme.onSecondaryContainer) }, onClick = {
viewModel.dispatch(ScreenAction.Notifications.Unmute)
})
@ -98,6 +99,8 @@ internal fun MessengerScreen(
})
}
}
}
})
when (state.composerState) {

View File

@ -8,7 +8,6 @@ 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.message.MessageOptionsStore
import app.dapk.st.domain.room.MutedRoomsStore
import app.dapk.st.engine.ChatEngine
import app.dapk.st.engine.MessengerPageState
import app.dapk.st.engine.RoomEvent
@ -27,7 +26,6 @@ internal fun messengerReducer(
copyToClipboard: CopyToClipboard,
deviceMeta: DeviceMeta,
messageOptionsStore: MessageOptionsStore,
mutedRoomsStore: MutedRoomsStore,
roomId: RoomId,
initialAttachments: List<MessageAttachment>?,
eventEmitter: suspend (MessengerEvent) -> Unit,
@ -38,7 +36,6 @@ internal fun messengerReducer(
roomState = Lce.Loading(),
composerState = initialComposerState(initialAttachments),
viewerState = null,
isMuted = false,
),
async(ComponentLifecycle::class) { action ->
@ -50,7 +47,6 @@ internal fun messengerReducer(
.onEach { dispatch(MessagesStateChange.Content(it)) }
.launchIn(coroutineScope)
)
dispatch(MessagesStateChange.MuteContent(mutedRoomsStore.isMuted(roomId)))
}
ComponentLifecycle.Gone -> jobBag.cancel("messages")
@ -141,15 +137,17 @@ 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}")
when (val roomState = state.roomState) {
is Lce.Content -> state.copy(roomState = roomState.copy(value = roomState.value.copy(isMuted = action.isMuted)))
is Lce.Error -> state
is Lce.Loading -> state
}
},
async(ScreenAction.Notifications::class) { action ->
when (action) {
ScreenAction.Notifications.Mute -> mutedRoomsStore.mute(roomId)
ScreenAction.Notifications.Unmute -> mutedRoomsStore.unmute(roomId)
ScreenAction.Notifications.Mute -> chatEngine.muteRoom(roomId)
ScreenAction.Notifications.Unmute -> chatEngine.unmuteRoom(roomId)
}
dispatch(

View File

@ -15,7 +15,6 @@ data class MessengerScreenState(
val roomState: Lce<MessengerPageState>,
val composerState: ComposerState,
val viewerState: ViewerState?,
val isMuted: Boolean,
)
data class ViewerState(

View File

@ -3,30 +3,28 @@ package app.dapk.st.engine
import app.dapk.st.core.Base64
import app.dapk.st.core.BuildMeta
import app.dapk.st.core.CoroutineDispatchers
import app.dapk.st.core.SingletonFlows
import app.dapk.st.core.extensions.ErrorTracker
import app.dapk.st.matrix.MatrixClient
import app.dapk.st.matrix.MatrixTaskRunner
import app.dapk.st.matrix.auth.DeviceDisplayNameGenerator
import app.dapk.st.matrix.auth.authService
import app.dapk.st.matrix.auth.installAuthService
import app.dapk.st.matrix.common.*
import app.dapk.st.matrix.crypto.*
import app.dapk.st.matrix.common.CredentialsStore
import app.dapk.st.matrix.common.MatrixLogger
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.crypto.MatrixMediaDecrypter
import app.dapk.st.matrix.crypto.cryptoService
import app.dapk.st.matrix.device.KnownDeviceStore
import app.dapk.st.matrix.device.deviceService
import app.dapk.st.matrix.device.installEncryptionService
import app.dapk.st.matrix.http.ktor.KtorMatrixHttpClientFactory
import app.dapk.st.matrix.message.*
import app.dapk.st.matrix.message.BackgroundScheduler
import app.dapk.st.matrix.message.LocalEchoStore
import app.dapk.st.matrix.message.internal.ImageContentReader
import app.dapk.st.matrix.push.installPushService
import app.dapk.st.matrix.message.messageService
import app.dapk.st.matrix.push.pushService
import app.dapk.st.matrix.room.*
import app.dapk.st.matrix.room.MemberStore
import app.dapk.st.matrix.room.ProfileStore
import app.dapk.st.matrix.room.profileService
import app.dapk.st.matrix.room.roomService
import app.dapk.st.matrix.sync.*
import app.dapk.st.matrix.sync.internal.request.ApiToDeviceEvent
import app.dapk.st.matrix.sync.internal.room.MessageDecrypter
import app.dapk.st.olm.DeviceKeyFactory
import app.dapk.st.olm.OlmStore
import app.dapk.st.olm.OlmWrapper
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@ -114,6 +112,10 @@ class MatrixEngine internal constructor(
override fun pushHandler() = matrixPushHandler.value
override suspend fun muteRoom(roomId: RoomId) = matrix.value.roomService().muteRoom(roomId)
override suspend fun unmuteRoom(roomId: RoomId) = matrix.value.roomService().unmuteRoom(roomId)
override suspend fun runTask(task: ChatEngineTask): TaskRunner.TaskResult {
return when (val result = matrix.value.run(MatrixTaskRunner.MatrixTask(task.type, task.jsonPayload))) {
is MatrixTaskRunner.TaskResult.Failure -> TaskRunner.TaskResult.Failure(result.canRetry)
@ -209,222 +211,4 @@ class MatrixEngine internal constructor(
}
object MatrixFactory {
fun createMatrix(
base64: Base64,
buildMeta: BuildMeta,
logger: MatrixLogger,
nameGenerator: DeviceDisplayNameGenerator,
coroutineDispatchers: CoroutineDispatchers,
errorTracker: ErrorTracker,
imageContentReader: ImageContentReader,
backgroundScheduler: BackgroundScheduler,
memberStore: MemberStore,
roomStore: RoomStore,
profileStore: ProfileStore,
syncStore: SyncStore,
overviewStore: OverviewStore,
filterStore: FilterStore,
localEchoStore: LocalEchoStore,
credentialsStore: CredentialsStore,
knownDeviceStore: KnownDeviceStore,
olmStore: OlmStore,
) = MatrixClient(
KtorMatrixHttpClientFactory(
credentialsStore,
includeLogging = buildMeta.isDebug,
),
logger
).also {
it.install {
installAuthService(credentialsStore, nameGenerator)
installEncryptionService(knownDeviceStore)
val singletonFlows = SingletonFlows(coroutineDispatchers)
val olm = OlmWrapper(
olmStore = olmStore,
singletonFlows = singletonFlows,
jsonCanonicalizer = JsonCanonicalizer(),
deviceKeyFactory = DeviceKeyFactory(JsonCanonicalizer()),
errorTracker = errorTracker,
logger = logger,
clock = Clock.systemUTC(),
coroutineDispatchers = coroutineDispatchers,
)
installCryptoService(
credentialsStore,
olm,
roomMembersProvider = { services ->
RoomMembersProvider {
services.roomService().joinedMembers(it).map { it.userId }
}
},
base64 = base64,
coroutineDispatchers = coroutineDispatchers,
)
installMessageService(
localEchoStore,
backgroundScheduler,
imageContentReader,
messageEncrypter = {
val cryptoService = it.cryptoService()
MessageEncrypter { message ->
val result = cryptoService.encrypt(
roomId = message.roomId,
credentials = credentialsStore.credentials()!!,
messageJson = message.contents,
)
MessageEncrypter.EncryptedMessagePayload(
result.algorithmName,
result.senderKey,
result.cipherText,
result.sessionId,
result.deviceId,
)
}
},
mediaEncrypter = {
val cryptoService = it.cryptoService()
MediaEncrypter { input ->
val result = cryptoService.encrypt(input)
MediaEncrypter.Result(
uri = result.uri,
contentLength = result.contentLength,
algorithm = result.algorithm,
ext = result.ext,
keyOperations = result.keyOperations,
kty = result.kty,
k = result.k,
iv = result.iv,
hashes = result.hashes,
v = result.v,
)
}
},
)
installRoomService(
memberStore,
roomMessenger = {
val messageService = it.messageService()
object : RoomMessenger {
override suspend fun enableEncryption(roomId: RoomId) {
messageService.sendEventMessage(
roomId, MessageService.EventMessage.Encryption(
algorithm = AlgorithmName("m.megolm.v1.aes-sha2")
)
)
}
}
},
roomInviteRemover = {
overviewStore.removeInvites(listOf(it))
}
)
installProfileService(profileStore, singletonFlows, credentialsStore)
installSyncService(
credentialsStore,
overviewStore,
roomStore,
syncStore,
filterStore,
deviceNotifier = { services ->
val encryption = services.deviceService()
val crypto = services.cryptoService()
DeviceNotifier { userIds, syncToken ->
encryption.updateStaleDevices(userIds)
crypto.updateOlmSession(userIds, syncToken)
}
},
messageDecrypter = { serviceProvider ->
val cryptoService = serviceProvider.cryptoService()
MessageDecrypter {
cryptoService.decrypt(it)
}
},
keySharer = { serviceProvider ->
val cryptoService = serviceProvider.cryptoService()
KeySharer { sharedRoomKeys ->
cryptoService.importRoomKeys(sharedRoomKeys)
}
},
verificationHandler = { services ->
val cryptoService = services.cryptoService()
VerificationHandler { apiEvent ->
logger.matrixLog(MatrixLogTag.VERIFICATION, "got a verification request $it")
cryptoService.onVerificationEvent(
when (apiEvent) {
is ApiToDeviceEvent.VerificationRequest -> Verification.Event.Requested(
apiEvent.sender,
apiEvent.content.fromDevice,
apiEvent.content.transactionId,
apiEvent.content.methods,
apiEvent.content.timestampPosix,
)
is ApiToDeviceEvent.VerificationReady -> Verification.Event.Ready(
apiEvent.sender,
apiEvent.content.fromDevice,
apiEvent.content.transactionId,
apiEvent.content.methods,
)
is ApiToDeviceEvent.VerificationStart -> Verification.Event.Started(
apiEvent.sender,
apiEvent.content.fromDevice,
apiEvent.content.method,
apiEvent.content.protocols,
apiEvent.content.hashes,
apiEvent.content.codes,
apiEvent.content.short,
apiEvent.content.transactionId,
)
is ApiToDeviceEvent.VerificationCancel -> TODO()
is ApiToDeviceEvent.VerificationAccept -> TODO()
is ApiToDeviceEvent.VerificationKey -> Verification.Event.Key(
apiEvent.sender,
apiEvent.content.transactionId,
apiEvent.content.key
)
is ApiToDeviceEvent.VerificationMac -> Verification.Event.Mac(
apiEvent.sender,
apiEvent.content.transactionId,
apiEvent.content.keys,
apiEvent.content.mac,
)
}
)
}
},
oneTimeKeyProducer = { services ->
val cryptoService = services.cryptoService()
MaybeCreateMoreKeys {
cryptoService.maybeCreateMoreKeys(it)
}
},
roomMembersService = { services ->
val roomService = services.roomService()
object : RoomMembersService {
override suspend fun find(roomId: RoomId, userIds: List<UserId>) = roomService.findMembers(roomId, userIds)
override suspend fun findSummary(roomId: RoomId) = roomService.findMembersSummary(roomId)
override suspend fun insert(roomId: RoomId, members: List<RoomMember>) = roomService.insertMembers(roomId, members)
}
},
errorTracker = errorTracker,
coroutineDispatchers = coroutineDispatchers,
)
installPushService(credentialsStore)
}
}
}
fun <T> unsafeLazy(initializer: () -> T): Lazy<T> = lazy(mode = LazyThreadSafetyMode.NONE, initializer = initializer)
private fun <T> unsafeLazy(initializer: () -> T): Lazy<T> = lazy(mode = LazyThreadSafetyMode.NONE, initializer = initializer)

View File

@ -0,0 +1,253 @@
package app.dapk.st.engine
import app.dapk.st.core.Base64
import app.dapk.st.core.BuildMeta
import app.dapk.st.core.CoroutineDispatchers
import app.dapk.st.core.SingletonFlows
import app.dapk.st.core.extensions.ErrorTracker
import app.dapk.st.matrix.MatrixClient
import app.dapk.st.matrix.auth.DeviceDisplayNameGenerator
import app.dapk.st.matrix.auth.installAuthService
import app.dapk.st.matrix.common.*
import app.dapk.st.matrix.crypto.RoomMembersProvider
import app.dapk.st.matrix.crypto.Verification
import app.dapk.st.matrix.crypto.cryptoService
import app.dapk.st.matrix.crypto.installCryptoService
import app.dapk.st.matrix.device.KnownDeviceStore
import app.dapk.st.matrix.device.deviceService
import app.dapk.st.matrix.device.installEncryptionService
import app.dapk.st.matrix.http.ktor.KtorMatrixHttpClientFactory
import app.dapk.st.matrix.message.*
import app.dapk.st.matrix.message.internal.ImageContentReader
import app.dapk.st.matrix.push.installPushService
import app.dapk.st.matrix.room.*
import app.dapk.st.matrix.room.internal.SingleRoomStore
import app.dapk.st.matrix.sync.*
import app.dapk.st.matrix.sync.internal.request.ApiToDeviceEvent
import app.dapk.st.matrix.sync.internal.room.MessageDecrypter
import app.dapk.st.olm.DeviceKeyFactory
import app.dapk.st.olm.OlmStore
import app.dapk.st.olm.OlmWrapper
import java.time.Clock
internal object MatrixFactory {
fun createMatrix(
base64: Base64,
buildMeta: BuildMeta,
logger: MatrixLogger,
nameGenerator: DeviceDisplayNameGenerator,
coroutineDispatchers: CoroutineDispatchers,
errorTracker: ErrorTracker,
imageContentReader: ImageContentReader,
backgroundScheduler: BackgroundScheduler,
memberStore: MemberStore,
roomStore: RoomStore,
profileStore: ProfileStore,
syncStore: SyncStore,
overviewStore: OverviewStore,
filterStore: FilterStore,
localEchoStore: LocalEchoStore,
credentialsStore: CredentialsStore,
knownDeviceStore: KnownDeviceStore,
olmStore: OlmStore,
) = MatrixClient(
KtorMatrixHttpClientFactory(
credentialsStore,
includeLogging = buildMeta.isDebug,
),
logger
).also {
it.install {
installAuthService(credentialsStore, nameGenerator)
installEncryptionService(knownDeviceStore)
val singletonFlows = SingletonFlows(coroutineDispatchers)
val olm = OlmWrapper(
olmStore = olmStore,
singletonFlows = singletonFlows,
jsonCanonicalizer = JsonCanonicalizer(),
deviceKeyFactory = DeviceKeyFactory(JsonCanonicalizer()),
errorTracker = errorTracker,
logger = logger,
clock = Clock.systemUTC(),
coroutineDispatchers = coroutineDispatchers,
)
installCryptoService(
credentialsStore,
olm,
roomMembersProvider = { services ->
RoomMembersProvider {
services.roomService().joinedMembers(it).map { it.userId }
}
},
base64 = base64,
coroutineDispatchers = coroutineDispatchers,
)
installMessageService(
localEchoStore,
backgroundScheduler,
imageContentReader,
messageEncrypter = {
val cryptoService = it.cryptoService()
MessageEncrypter { message ->
val result = cryptoService.encrypt(
roomId = message.roomId,
credentials = credentialsStore.credentials()!!,
messageJson = message.contents,
)
MessageEncrypter.EncryptedMessagePayload(
result.algorithmName,
result.senderKey,
result.cipherText,
result.sessionId,
result.deviceId,
)
}
},
mediaEncrypter = {
val cryptoService = it.cryptoService()
MediaEncrypter { input ->
val result = cryptoService.encrypt(input)
MediaEncrypter.Result(
uri = result.uri,
contentLength = result.contentLength,
algorithm = result.algorithm,
ext = result.ext,
keyOperations = result.keyOperations,
kty = result.kty,
k = result.k,
iv = result.iv,
hashes = result.hashes,
v = result.v,
)
}
},
)
installRoomService(
memberStore,
roomMessenger = {
val messageService = it.messageService()
object : RoomMessenger {
override suspend fun enableEncryption(roomId: RoomId) {
messageService.sendEventMessage(
roomId, MessageService.EventMessage.Encryption(
algorithm = AlgorithmName("m.megolm.v1.aes-sha2")
)
)
}
}
},
roomInviteRemover = {
overviewStore.removeInvites(listOf(it))
},
singleRoomStore = object : SingleRoomStore {
override suspend fun mute(roomId: RoomId) = roomStore.mute(roomId)
override suspend fun unmute(roomId: RoomId) = roomStore.unmute(roomId)
override suspend fun isMuted(roomId: RoomId) = roomStore.isMuted(roomId)
}
)
installProfileService(profileStore, singletonFlows, credentialsStore)
installSyncService(
credentialsStore,
overviewStore,
roomStore,
syncStore,
filterStore,
deviceNotifier = { services ->
val encryption = services.deviceService()
val crypto = services.cryptoService()
DeviceNotifier { userIds, syncToken ->
encryption.updateStaleDevices(userIds)
crypto.updateOlmSession(userIds, syncToken)
}
},
messageDecrypter = { serviceProvider ->
val cryptoService = serviceProvider.cryptoService()
MessageDecrypter {
cryptoService.decrypt(it)
}
},
keySharer = { serviceProvider ->
val cryptoService = serviceProvider.cryptoService()
KeySharer { sharedRoomKeys ->
cryptoService.importRoomKeys(sharedRoomKeys)
}
},
verificationHandler = { services ->
val cryptoService = services.cryptoService()
VerificationHandler { apiEvent ->
logger.matrixLog(MatrixLogTag.VERIFICATION, "got a verification request $it")
cryptoService.onVerificationEvent(
when (apiEvent) {
is ApiToDeviceEvent.VerificationRequest -> Verification.Event.Requested(
apiEvent.sender,
apiEvent.content.fromDevice,
apiEvent.content.transactionId,
apiEvent.content.methods,
apiEvent.content.timestampPosix,
)
is ApiToDeviceEvent.VerificationReady -> Verification.Event.Ready(
apiEvent.sender,
apiEvent.content.fromDevice,
apiEvent.content.transactionId,
apiEvent.content.methods,
)
is ApiToDeviceEvent.VerificationStart -> Verification.Event.Started(
apiEvent.sender,
apiEvent.content.fromDevice,
apiEvent.content.method,
apiEvent.content.protocols,
apiEvent.content.hashes,
apiEvent.content.codes,
apiEvent.content.short,
apiEvent.content.transactionId,
)
is ApiToDeviceEvent.VerificationCancel -> TODO()
is ApiToDeviceEvent.VerificationAccept -> TODO()
is ApiToDeviceEvent.VerificationKey -> Verification.Event.Key(
apiEvent.sender,
apiEvent.content.transactionId,
apiEvent.content.key
)
is ApiToDeviceEvent.VerificationMac -> Verification.Event.Mac(
apiEvent.sender,
apiEvent.content.transactionId,
apiEvent.content.keys,
apiEvent.content.mac,
)
}
)
}
},
oneTimeKeyProducer = { services ->
val cryptoService = services.cryptoService()
MaybeCreateMoreKeys {
cryptoService.maybeCreateMoreKeys(it)
}
},
roomMembersService = { services ->
val roomService = services.roomService()
object : RoomMembersService {
override suspend fun find(roomId: RoomId, userIds: List<UserId>) = roomService.findMembers(roomId, userIds)
override suspend fun findSummary(roomId: RoomId) = roomService.findMembersSummary(roomId)
override suspend fun insert(roomId: RoomId, members: List<RoomMember>) = roomService.insertMembers(roomId, members)
}
},
errorTracker = errorTracker,
coroutineDispatchers = coroutineDispatchers,
)
installPushService(credentialsStore)
}
}
}

View File

@ -38,6 +38,7 @@ internal class TimelineUseCaseImpl(
},
typing = events.filterIsInstance<SyncService.SyncEvent.Typing>().firstOrNull { it.roomId == roomId }?.engine(),
self = userId,
isMuted = roomService.isMuted(roomId)
)
}
}

View File

@ -5,10 +5,7 @@ import app.dapk.st.matrix.common.EventId
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.RoomMember
import app.dapk.st.matrix.common.UserId
import app.dapk.st.matrix.room.internal.DefaultRoomService
import app.dapk.st.matrix.room.internal.RoomInviteRemover
import app.dapk.st.matrix.room.internal.RoomMembers
import app.dapk.st.matrix.room.internal.RoomMembersCache
import app.dapk.st.matrix.room.internal.*
private val SERVICE_KEY = RoomService::class
@ -27,6 +24,10 @@ interface RoomService : MatrixService {
suspend fun joinRoom(roomId: RoomId)
suspend fun rejectJoinRoom(roomId: RoomId)
suspend fun muteRoom(roomId: RoomId)
suspend fun unmuteRoom(roomId: RoomId)
suspend fun isMuted(roomId: RoomId): Boolean
data class JoinedMember(
val userId: UserId,
val displayName: String?,
@ -39,6 +40,7 @@ fun MatrixServiceInstaller.installRoomService(
memberStore: MemberStore,
roomMessenger: ServiceDepFactory<RoomMessenger>,
roomInviteRemover: RoomInviteRemover,
singleRoomStore: SingleRoomStore,
): InstallExtender<RoomService> {
return this.install { (httpClient, _, services, logger) ->
SERVICE_KEY to DefaultRoomService(
@ -46,7 +48,8 @@ fun MatrixServiceInstaller.installRoomService(
logger,
RoomMembers(memberStore, RoomMembersCache()),
roomMessenger.create(services),
roomInviteRemover
roomInviteRemover,
singleRoomStore,
)
}
}

View File

@ -19,6 +19,7 @@ class DefaultRoomService(
private val roomMembers: RoomMembers,
private val roomMessenger: RoomMessenger,
private val roomInviteRemover: RoomInviteRemover,
private val singleRoomStore: SingleRoomStore,
) : RoomService {
override suspend fun joinedMembers(roomId: RoomId): List<RoomService.JoinedMember> {
@ -82,6 +83,7 @@ class DefaultRoomService(
} else {
throw it
}
}
else -> throw it
@ -90,6 +92,22 @@ class DefaultRoomService(
)
roomInviteRemover.remove(roomId)
}
override suspend fun muteRoom(roomId: RoomId) {
singleRoomStore.mute(roomId)
}
override suspend fun unmuteRoom(roomId: RoomId) {
singleRoomStore.unmute(roomId)
}
override suspend fun isMuted(roomId: RoomId) = singleRoomStore.isMuted(roomId)
}
interface SingleRoomStore {
suspend fun mute(roomId: RoomId)
suspend fun unmute(roomId: RoomId)
suspend fun isMuted(roomId: RoomId): Boolean
}
internal fun joinedMembersRequest(roomId: RoomId) = httpRequest<JoinedMembersResponse>(

View File

@ -5,7 +5,7 @@ import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.SyncToken
import kotlinx.coroutines.flow.Flow
interface RoomStore {
interface RoomStore : MuteableStore {
suspend fun persist(roomId: RoomId, events: List<RoomEvent>)
suspend fun remove(rooms: List<RoomId>)
@ -21,6 +21,14 @@ interface RoomStore {
}
interface MuteableStore {
suspend fun mute(roomId: RoomId)
suspend fun unmute(roomId: RoomId)
suspend fun isMuted(roomId: RoomId): Boolean
suspend fun allMuted(): Set<RoomId>
}
interface FilterStore {
suspend fun store(key: String, filterId: String)