diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt index 6c0e730499..d930a5d0fd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt @@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.send.DraftService import org.matrix.android.sdk.api.session.room.send.SendService import org.matrix.android.sdk.api.session.room.state.StateService import org.matrix.android.sdk.api.session.room.tags.TagsService +import org.matrix.android.sdk.api.session.room.threads.ThreadsService import org.matrix.android.sdk.api.session.room.timeline.TimelineService import org.matrix.android.sdk.api.session.room.typing.TypingService import org.matrix.android.sdk.api.session.room.uploads.UploadsService @@ -45,6 +46,7 @@ import org.matrix.android.sdk.api.util.Optional */ interface Room : TimelineService, + ThreadsService, SendService, DraftService, ReadService, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/threads/ThreadsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/threads/ThreadsService.kt new file mode 100644 index 0000000000..34a50a8951 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/threads/ThreadsService.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.session.room.threads + +import androidx.lifecycle.LiveData +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent + +/** + * This interface defines methods to interact with threads related features. It's implemented at the room level within the main timeline. + */ +interface ThreadsService { + + /** + * Get a live list of all the TimelineEvents which have thread replies for the specified roomId + * @return the [LiveData] of [TimelineEvent] + */ + fun getAllThreadsLive(): LiveData> + + /** + * Get a list of all the TimelineEvents which have thread replies for the specified roomId + * @return the [LiveData] of [TimelineEvent] + */ + fun getAllThreads(): List + + /** + * Get a live list of all the local unread threads for the specified roomId + * @return the [LiveData] of [TimelineEvent] + */ + fun getNumberOfLocalThreadNotificationsLive(): LiveData> + + /** + * Get a list of all the local unread threads for the specified roomId + * @return the [LiveData] of [TimelineEvent] + */ + fun getNumberOfLocalThreadNotifications(): List + + /** + * Returns whether or not the current user is participating in the thread + * @param rootThreadEventId the eventId of the current thread + */ + fun isUserParticipatingInThread(rootThreadEventId: String): Boolean + + /** + * Enhance the thread list with the edited events if needed + * @return the [LiveData] of [TimelineEvent] + */ + fun mapEventsWithEdition(threads: List): List + + /** + * Marks the current thread as read. This is a local implementation + * @param rootThreadEventId the eventId of the current thread + */ + suspend fun markThreadAsRead(rootThreadEventId: String) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt index c9ccf9ca51..3c021384e1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt @@ -55,46 +55,4 @@ interface TimelineService { * Returns a snapshot list of TimelineEvent with EventType.MESSAGE and MessageType.MSGTYPE_IMAGE or MessageType.MSGTYPE_VIDEO. */ fun getAttachmentMessages(): List - - /** - * Get a live list of all the TimelineEvents which have thread replies for the specified roomId - * @return the [LiveData] of [TimelineEvent] - */ - fun getAllThreadsLive(): LiveData> - - /** - * Get a list of all the TimelineEvents which have thread replies for the specified roomId - * @return the [LiveData] of [TimelineEvent] - */ - fun getAllThreads(): List - - /** - * Get a live list of all the local unread threads for the specified roomId - * @return the [LiveData] of [TimelineEvent] - */ - fun getNumberOfLocalThreadNotificationsLive(): LiveData> - - /** - * Get a list of all the local unread threads for the specified roomId - * @return the [LiveData] of [TimelineEvent] - */ - fun getNumberOfLocalThreadNotifications(): List - - /** - * Returns whether or not the current user is participating in the thread - * @param rootThreadEventId the eventId of the current thread - */ - fun isUserParticipatingInThread(rootThreadEventId: String): Boolean - - /** - * Enhance the thread list with the edited events if needed - * @return the [LiveData] of [TimelineEvent] - */ - fun mapEventsWithEdition(threads: List): List - - /** - * Marks the current thread as read. This is a local implementation - * @param rootThreadEventId the eventId of the current thread - */ - suspend fun markThreadAsRead(rootThreadEventId: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt index 1c3d1971c2..2d8c3e9c78 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt @@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.room.send.DraftService import org.matrix.android.sdk.api.session.room.send.SendService import org.matrix.android.sdk.api.session.room.state.StateService import org.matrix.android.sdk.api.session.room.tags.TagsService +import org.matrix.android.sdk.api.session.room.threads.ThreadsService import org.matrix.android.sdk.api.session.room.timeline.TimelineService import org.matrix.android.sdk.api.session.room.typing.TypingService import org.matrix.android.sdk.api.session.room.uploads.UploadsService @@ -54,6 +55,7 @@ import java.security.InvalidParameterException internal class DefaultRoom(override val roomId: String, private val roomSummaryDataSource: RoomSummaryDataSource, private val timelineService: TimelineService, + private val threadsService: ThreadsService, private val sendService: SendService, private val draftService: DraftService, private val stateService: StateService, @@ -77,6 +79,7 @@ internal class DefaultRoom(override val roomId: String, ) : Room, TimelineService by timelineService, + ThreadsService by threadsService, SendService by sendService, DraftService by draftService, StateService by stateService, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt index 4ab06338a2..70c1ab4f42 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt @@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.session.room.state.DefaultStateService import org.matrix.android.sdk.internal.session.room.state.SendStateTask import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource import org.matrix.android.sdk.internal.session.room.tags.DefaultTagsService +import org.matrix.android.sdk.internal.session.room.threads.DefaultThreadsService import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimelineService import org.matrix.android.sdk.internal.session.room.typing.DefaultTypingService import org.matrix.android.sdk.internal.session.room.uploads.DefaultUploadsService @@ -50,6 +51,7 @@ internal interface RoomFactory { internal class DefaultRoomFactory @Inject constructor(private val cryptoService: CryptoService, private val roomSummaryDataSource: RoomSummaryDataSource, private val timelineServiceFactory: DefaultTimelineService.Factory, + private val threadsServiceFactory: DefaultThreadsService.Factory, private val sendServiceFactory: DefaultSendService.Factory, private val draftServiceFactory: DefaultDraftService.Factory, private val stateServiceFactory: DefaultStateService.Factory, @@ -76,6 +78,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService: roomId = roomId, roomSummaryDataSource = roomSummaryDataSource, timelineService = timelineServiceFactory.create(roomId), + threadsService = threadsServiceFactory.create(roomId), sendService = sendServiceFactory.create(roomId), draftService = draftServiceFactory.create(roomId), stateService = stateServiceFactory.create(roomId), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/threads/DefaultThreadsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/threads/DefaultThreadsService.kt new file mode 100644 index 0000000000..e6fe5e6a8b --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/threads/DefaultThreadsService.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.threads + +import androidx.lifecycle.LiveData +import com.zhuinden.monarchy.Monarchy +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.realm.Realm +import org.matrix.android.sdk.api.session.room.threads.ThreadsService +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.session.threads.ThreadNotificationState +import org.matrix.android.sdk.internal.database.helper.findAllLocalThreadNotificationsForRoomId +import org.matrix.android.sdk.internal.database.helper.findAllThreadsForRoomId +import org.matrix.android.sdk.internal.database.helper.isUserParticipatingInThread +import org.matrix.android.sdk.internal.database.helper.mapEventsWithEdition +import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper +import org.matrix.android.sdk.internal.database.model.EventEntity +import org.matrix.android.sdk.internal.database.model.TimelineEventEntity +import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.util.awaitTransaction + +internal class DefaultThreadsService @AssistedInject constructor( + @Assisted private val roomId: String, + @UserId private val userId: String, + @SessionDatabase private val monarchy: Monarchy, + private val timelineEventMapper: TimelineEventMapper, +) : ThreadsService { + + @AssistedFactory + interface Factory { + fun create(roomId: String): DefaultThreadsService + } + + override fun getNumberOfLocalThreadNotificationsLive(): LiveData> { + return monarchy.findAllMappedWithChanges( + { TimelineEventEntity.findAllLocalThreadNotificationsForRoomId(it, roomId = roomId) }, + { timelineEventMapper.map(it) } + ) + } + + override fun getNumberOfLocalThreadNotifications(): List { + return monarchy.fetchAllMappedSync( + { TimelineEventEntity.findAllLocalThreadNotificationsForRoomId(it, roomId = roomId) }, + { timelineEventMapper.map(it) } + ) + } + + override fun getAllThreadsLive(): LiveData> { + return monarchy.findAllMappedWithChanges( + { TimelineEventEntity.findAllThreadsForRoomId(it, roomId = roomId) }, + { timelineEventMapper.map(it) } + ) + } + + override fun getAllThreads(): List { + return monarchy.fetchAllMappedSync( + { TimelineEventEntity.findAllThreadsForRoomId(it, roomId = roomId) }, + { timelineEventMapper.map(it) } + ) + } + + override fun isUserParticipatingInThread(rootThreadEventId: String): Boolean { + return Realm.getInstance(monarchy.realmConfiguration).use { + TimelineEventEntity.isUserParticipatingInThread( + realm = it, + roomId = roomId, + rootThreadEventId = rootThreadEventId, + senderId = userId) + } + } + + override fun mapEventsWithEdition(threads: List): List { + return Realm.getInstance(monarchy.realmConfiguration).use { + threads.mapEventsWithEdition(it, roomId) + } + } + + override suspend fun markThreadAsRead(rootThreadEventId: String) { + monarchy.awaitTransaction { + EventEntity.where( + realm = it, + eventId = rootThreadEventId).findFirst()?.threadNotificationState = ThreadNotificationState.NO_NEW_MESSAGE + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt index cc6485698b..748231fc73 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt @@ -31,16 +31,10 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineService import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings -import org.matrix.android.sdk.api.session.threads.ThreadNotificationState import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.database.RealmSessionProvider -import org.matrix.android.sdk.internal.database.helper.findAllLocalThreadNotificationsForRoomId -import org.matrix.android.sdk.internal.database.helper.findAllThreadsForRoomId -import org.matrix.android.sdk.internal.database.helper.isUserParticipatingInThread -import org.matrix.android.sdk.internal.database.helper.mapEventsWithEdition import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper -import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.query.where @@ -50,7 +44,6 @@ import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTa import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.util.awaitTransaction internal class DefaultTimelineService @AssistedInject constructor( @Assisted private val roomId: String, @@ -119,56 +112,4 @@ internal class DefaultTimelineService @AssistedInject constructor( .orEmpty() } } - - override fun getNumberOfLocalThreadNotificationsLive(): LiveData> { - return monarchy.findAllMappedWithChanges( - { TimelineEventEntity.findAllLocalThreadNotificationsForRoomId(it, roomId = roomId) }, - { timelineEventMapper.map(it) } - ) - } - - override fun getNumberOfLocalThreadNotifications(): List { - return monarchy.fetchAllMappedSync( - { TimelineEventEntity.findAllLocalThreadNotificationsForRoomId(it, roomId = roomId) }, - { timelineEventMapper.map(it) } - ) - } - - override fun getAllThreadsLive(): LiveData> { - return monarchy.findAllMappedWithChanges( - { TimelineEventEntity.findAllThreadsForRoomId(it, roomId = roomId) }, - { timelineEventMapper.map(it) } - ) - } - - override fun getAllThreads(): List { - return monarchy.fetchAllMappedSync( - { TimelineEventEntity.findAllThreadsForRoomId(it, roomId = roomId) }, - { timelineEventMapper.map(it) } - ) - } - - override fun isUserParticipatingInThread(rootThreadEventId: String): Boolean { - return Realm.getInstance(monarchy.realmConfiguration).use { - TimelineEventEntity.isUserParticipatingInThread( - realm = it, - roomId = roomId, - rootThreadEventId = rootThreadEventId, - senderId = userId) - } - } - - override fun mapEventsWithEdition(threads: List): List { - return Realm.getInstance(monarchy.realmConfiguration).use { - threads.mapEventsWithEdition(it, roomId) - } - } - - override suspend fun markThreadAsRead(rootThreadEventId: String) { - monarchy.awaitTransaction { - EventEntity.where( - realm = it, - eventId = rootThreadEventId).findFirst()?.threadNotificationState = ThreadNotificationState.NO_NEW_MESSAGE - } - } }