From f0a856eb20efa0c1a1dc94b31e98483df7115e0e Mon Sep 17 00:00:00 2001 From: Adam Brown <adampsbrown@gmail.com> Date: Thu, 17 Mar 2022 23:01:40 +0000 Subject: [PATCH] adding room join/leaving based on sync status - means the invitations page should work! --- .../st/domain/sync/OverviewPersistence.kt | 20 ++++++++++++++++ .../app/dapk/db/model/InviteState.sq | 6 ++++- .../app/dapk/db/model/OverviewState.sq | 6 ++++- .../sqldelight/app/dapk/db/model/RoomEvent.sq | 4 ++++ .../st/notifications/NotificationsUseCase.kt | 3 ++- .../st/notifications/PushAndroidService.kt | 2 ++ .../app/dapk/st/profile/ProfileViewModel.kt | 24 ++++++++++++++----- .../kotlin/app/dapk/st/matrix/sync/Store.kt | 2 ++ .../sync/internal/request/ApiSyncResponse.kt | 1 + .../matrix/sync/internal/sync/SyncReducer.kt | 13 ++++++++-- .../matrix/sync/internal/sync/SyncUseCase.kt | 12 +++++++++- 11 files changed, 81 insertions(+), 12 deletions(-) diff --git a/domains/store/src/main/kotlin/app/dapk/st/domain/sync/OverviewPersistence.kt b/domains/store/src/main/kotlin/app/dapk/st/domain/sync/OverviewPersistence.kt index a6083e5..f20ba4b 100644 --- a/domains/store/src/main/kotlin/app/dapk/st/domain/sync/OverviewPersistence.kt +++ b/domains/store/src/main/kotlin/app/dapk/st/domain/sync/OverviewPersistence.kt @@ -29,6 +29,18 @@ internal class OverviewPersistence( .map { it.map { json.decodeFromString(RoomOverview.serializer(), it.blob) } } } + override suspend fun removeRooms(roomsToRemove: List<RoomId>) { + dispatchers.withIoContext { + database.transaction { + roomsToRemove.forEach { + database.inviteStateQueries.remove(it.value) + database.overviewStateQueries.remove(it.value) + database.roomEventQueries.remove(it.value) + } + } + } + } + override suspend fun persistInvites(invites: List<RoomInvite>) { dispatchers.withIoContext { database.inviteStateQueries.transaction { @@ -46,6 +58,14 @@ internal class OverviewPersistence( .map { it.map { json.decodeFromString(RoomInvite.serializer(), it.blob) } } } + override suspend fun removeInvites(invites: List<RoomId>) { + dispatchers.withIoContext { + database.inviteStateQueries.transaction { + invites.forEach { database.inviteStateQueries.remove(it.value) } + } + } + } + override suspend fun persist(overviewState: OverviewState) { dispatchers.withIoContext { database.transaction { diff --git a/domains/store/src/main/sqldelight/app/dapk/db/model/InviteState.sq b/domains/store/src/main/sqldelight/app/dapk/db/model/InviteState.sq index 67be2b1..d30ddbe 100644 --- a/domains/store/src/main/sqldelight/app/dapk/db/model/InviteState.sq +++ b/domains/store/src/main/sqldelight/app/dapk/db/model/InviteState.sq @@ -10,4 +10,8 @@ FROM dbInviteState; insert: INSERT OR REPLACE INTO dbInviteState(room_id, blob) -VALUES (?, ?); \ No newline at end of file +VALUES (?, ?); + +remove: +DELETE FROM dbInviteState +WHERE room_id = ?; \ No newline at end of file diff --git a/domains/store/src/main/sqldelight/app/dapk/db/model/OverviewState.sq b/domains/store/src/main/sqldelight/app/dapk/db/model/OverviewState.sq index afc9da1..100d397 100644 --- a/domains/store/src/main/sqldelight/app/dapk/db/model/OverviewState.sq +++ b/domains/store/src/main/sqldelight/app/dapk/db/model/OverviewState.sq @@ -18,4 +18,8 @@ WHERE room_id = ?; insert: INSERT OR REPLACE INTO dbOverviewState(room_id, latest_activity_timestamp_utc, read_marker, blob) -VALUES (?, ?, ?, ?); \ No newline at end of file +VALUES (?, ?, ?, ?); + +remove: +DELETE FROM dbOverviewState +WHERE room_id = ?; \ No newline at end of file diff --git a/domains/store/src/main/sqldelight/app/dapk/db/model/RoomEvent.sq b/domains/store/src/main/sqldelight/app/dapk/db/model/RoomEvent.sq index 6c10a56..46883cf 100644 --- a/domains/store/src/main/sqldelight/app/dapk/db/model/RoomEvent.sq +++ b/domains/store/src/main/sqldelight/app/dapk/db/model/RoomEvent.sq @@ -33,3 +33,7 @@ FROM dbUnreadEvent INNER JOIN dbRoomEvent ON dbUnreadEvent.event_id = dbRoomEvent.event_id ORDER BY dbRoomEvent.timestamp_utc DESC LIMIT 100; + +remove: +DELETE FROM dbRoomEvent +WHERE room_id = ?; \ No newline at end of file diff --git a/features/notifications/src/main/kotlin/app/dapk/st/notifications/NotificationsUseCase.kt b/features/notifications/src/main/kotlin/app/dapk/st/notifications/NotificationsUseCase.kt index ef2d8c2..87cd027 100644 --- a/features/notifications/src/main/kotlin/app/dapk/st/notifications/NotificationsUseCase.kt +++ b/features/notifications/src/main/kotlin/app/dapk/st/notifications/NotificationsUseCase.kt @@ -32,7 +32,8 @@ class NotificationsUseCase( val asRooms = changes.keys val removedRooms = inferredCurrentNotifications.keys - asRooms - val onlyContainsRemovals = inferredCurrentNotifications.filterKeys { !removedRooms.contains(it) } == changes.filterKeys { !removedRooms.contains(it) } + val onlyContainsRemovals = + inferredCurrentNotifications.filterKeys { !removedRooms.contains(it) } == changes.filterKeys { !removedRooms.contains(it) } inferredCurrentNotifications.clear() inferredCurrentNotifications.putAll(changes) diff --git a/features/notifications/src/main/kotlin/app/dapk/st/notifications/PushAndroidService.kt b/features/notifications/src/main/kotlin/app/dapk/st/notifications/PushAndroidService.kt index 8b845c7..5c6a0c9 100644 --- a/features/notifications/src/main/kotlin/app/dapk/st/notifications/PushAndroidService.kt +++ b/features/notifications/src/main/kotlin/app/dapk/st/notifications/PushAndroidService.kt @@ -28,8 +28,10 @@ class PushAndroidService : FirebaseMessagingService() { } override fun onNewToken(token: String) { + log(PUSH, "new push token received") GlobalScope.launch { module.pushUseCase().registerPush(token) + log(PUSH, "token registered") } } diff --git a/features/profile/src/main/kotlin/app/dapk/st/profile/ProfileViewModel.kt b/features/profile/src/main/kotlin/app/dapk/st/profile/ProfileViewModel.kt index 3a883db..484cdcf 100644 --- a/features/profile/src/main/kotlin/app/dapk/st/profile/ProfileViewModel.kt +++ b/features/profile/src/main/kotlin/app/dapk/st/profile/ProfileViewModel.kt @@ -79,15 +79,15 @@ class ProfileViewModel( } fun acceptRoomInvite(roomId: RoomId) { - viewModelScope.launch { - roomService.joinRoom(roomId) - } + launchCatching { roomService.joinRoom(roomId) }.fold( + onError = {} + ) } fun rejectRoomInvite(roomId: RoomId) { - viewModelScope.launch { - roomService.rejectJoinRoom(roomId) - } + launchCatching { roomService.rejectJoinRoom(roomId) }.fold( + onError = {} + ) } fun stop() { @@ -103,3 +103,15 @@ class ProfileViewModel( } } + +fun <S, VE, T> DapkViewModel<S, VE>.launchCatching(block: suspend () -> T): LaunchCatching<T> { + return object : LaunchCatching<T> { + override fun fold(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit) { + viewModelScope.launch { runCatching { block() }.fold(onSuccess, onError) } + } + } +} + +interface LaunchCatching<T> { + fun fold(onSuccess: (T) -> Unit = {}, onError: (Throwable) -> Unit = {}) +} \ No newline at end of file diff --git a/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/Store.kt b/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/Store.kt index 97ddf63..f2ba13a 100644 --- a/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/Store.kt +++ b/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/Store.kt @@ -28,6 +28,7 @@ interface FilterStore { interface OverviewStore { + suspend fun removeRooms(roomsToRemove: List<RoomId>) suspend fun persistInvites(invite: List<RoomInvite>) suspend fun persist(overviewState: OverviewState) @@ -35,6 +36,7 @@ interface OverviewStore { fun latest(): Flow<OverviewState> fun latestInvites(): Flow<List<RoomInvite>> + suspend fun removeInvites(map: List<RoomId>) } interface SyncStore { diff --git a/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/request/ApiSyncResponse.kt b/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/request/ApiSyncResponse.kt index 54414cb..b125924 100644 --- a/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/request/ApiSyncResponse.kt +++ b/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/request/ApiSyncResponse.kt @@ -214,6 +214,7 @@ sealed class ApiToDeviceEvent { internal data class ApiSyncRooms( @SerialName("join") val join: Map<RoomId, ApiSyncRoom>? = null, @SerialName("invite") val invite: Map<RoomId, ApiSyncRoomInvite>? = null, + @SerialName("leave") val leave: Map<RoomId, ApiSyncRoom>? = null, ) @Serializable diff --git a/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/sync/SyncReducer.kt b/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/sync/SyncReducer.kt index f9443b4..0b84495 100644 --- a/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/sync/SyncReducer.kt +++ b/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/sync/SyncReducer.kt @@ -20,13 +20,16 @@ internal class SyncReducer( data class ReducerResult( val roomState: List<RoomState>, - val invites: List<RoomInvite> + val invites: List<RoomInvite>, + val roomsLeft: List<RoomId> ) suspend fun reduce(isInitialSync: Boolean, sideEffects: SideEffectResult, response: ApiSyncResponse, userCredentials: UserCredentials): ReducerResult { val directMessages = response.directMessages() val invites = response.rooms?.invite?.map { roomInvite(it, userCredentials) } ?: emptyList() + val roomsLeft = findRoomsLeft(response, userCredentials) + val apiUpdatedRooms = response.rooms?.join?.keepRoomsWithChanges() val apiRoomsToProcess = apiUpdatedRooms?.map { (roomId, apiRoom) -> logger.matrixLog(SYNC, "reducing: $roomId") @@ -49,9 +52,15 @@ internal class SyncReducer( } } - return ReducerResult((apiRoomsToProcess + roomsWithSideEffects).awaitAll().filterNotNull(), invites) + return ReducerResult((apiRoomsToProcess + roomsWithSideEffects).awaitAll().filterNotNull(), invites, roomsLeft) } + private fun findRoomsLeft(response: ApiSyncResponse, userCredentials: UserCredentials) = response.rooms?.leave?.filter { + it.value.state.stateEvents.filterIsInstance<ApiTimelineEvent.RoomMember>().any { + it.content.membership.isLeave() && it.senderId == userCredentials.userId + } + }?.map { it.key } ?: emptyList() + private fun roomInvite(entry: Map.Entry<RoomId, ApiSyncRoomInvite>, userCredentials: UserCredentials): RoomInvite { val memberEvents = entry.value.state.events.filterIsInstance<ApiStrippedEvent.RoomMember>() val invitee = memberEvents.first { it.content.membership?.isInvite() ?: false } diff --git a/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/sync/SyncUseCase.kt b/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/sync/SyncUseCase.kt index d56e8a8..ecf0058 100644 --- a/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/sync/SyncUseCase.kt +++ b/matrix/services/sync/src/main/kotlin/app/dapk/st/matrix/sync/internal/sync/SyncUseCase.kt @@ -46,13 +46,23 @@ internal class SyncUseCase( val nextState = logger.logP("reducing") { syncReducer.reduce(isInitialSync, sideEffects, response, credentials) } val overview = nextState.roomState.map { it.roomOverview } + if (nextState.roomsLeft.isNotEmpty()) { + persistence.removeRooms(nextState.roomsLeft) + } if (nextState.invites.isNotEmpty()) { persistence.persistInvites(nextState.invites) } + when { previousState == overview -> previousState.also { logger.matrixLog(SYNC, "no changes, not persisting new state") } - overview.isNotEmpty() -> overview.also { persistence.persist(overview) } + overview.isNotEmpty() -> overview.also { + val newRooms = overview - (previousState ?: emptyList()).toSet() + if (newRooms.isNotEmpty()) { + persistence.removeInvites(newRooms.map { it.roomId }) + } + persistence.persist(overview) + } else -> previousState.also { logger.matrixLog(SYNC, "nothing to do") } } }