Introduce ThreadsService by splitting TimelineService

This commit is contained in:
ariskotsomitopoulos 2022-01-31 12:58:19 +02:00
parent 5ff5f762d4
commit 3253a252fb
7 changed files with 179 additions and 101 deletions

View file

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

View file

@ -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<List<TimelineEvent>>
/**
* Get a list of all the TimelineEvents which have thread replies for the specified roomId
* @return the [LiveData] of [TimelineEvent]
*/
fun getAllThreads(): List<TimelineEvent>
/**
* Get a live list of all the local unread threads for the specified roomId
* @return the [LiveData] of [TimelineEvent]
*/
fun getNumberOfLocalThreadNotificationsLive(): LiveData<List<TimelineEvent>>
/**
* Get a list of all the local unread threads for the specified roomId
* @return the [LiveData] of [TimelineEvent]
*/
fun getNumberOfLocalThreadNotifications(): List<TimelineEvent>
/**
* 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<TimelineEvent>): List<TimelineEvent>
/**
* Marks the current thread as read. This is a local implementation
* @param rootThreadEventId the eventId of the current thread
*/
suspend fun markThreadAsRead(rootThreadEventId: String)
}

View file

@ -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<TimelineEvent>
/**
* Get a live list of all the TimelineEvents which have thread replies for the specified roomId
* @return the [LiveData] of [TimelineEvent]
*/
fun getAllThreadsLive(): LiveData<List<TimelineEvent>>
/**
* Get a list of all the TimelineEvents which have thread replies for the specified roomId
* @return the [LiveData] of [TimelineEvent]
*/
fun getAllThreads(): List<TimelineEvent>
/**
* Get a live list of all the local unread threads for the specified roomId
* @return the [LiveData] of [TimelineEvent]
*/
fun getNumberOfLocalThreadNotificationsLive(): LiveData<List<TimelineEvent>>
/**
* Get a list of all the local unread threads for the specified roomId
* @return the [LiveData] of [TimelineEvent]
*/
fun getNumberOfLocalThreadNotifications(): List<TimelineEvent>
/**
* 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<TimelineEvent>): List<TimelineEvent>
/**
* Marks the current thread as read. This is a local implementation
* @param rootThreadEventId the eventId of the current thread
*/
suspend fun markThreadAsRead(rootThreadEventId: String)
}

View file

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

View file

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

View file

@ -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<List<TimelineEvent>> {
return monarchy.findAllMappedWithChanges(
{ TimelineEventEntity.findAllLocalThreadNotificationsForRoomId(it, roomId = roomId) },
{ timelineEventMapper.map(it) }
)
}
override fun getNumberOfLocalThreadNotifications(): List<TimelineEvent> {
return monarchy.fetchAllMappedSync(
{ TimelineEventEntity.findAllLocalThreadNotificationsForRoomId(it, roomId = roomId) },
{ timelineEventMapper.map(it) }
)
}
override fun getAllThreadsLive(): LiveData<List<TimelineEvent>> {
return monarchy.findAllMappedWithChanges(
{ TimelineEventEntity.findAllThreadsForRoomId(it, roomId = roomId) },
{ timelineEventMapper.map(it) }
)
}
override fun getAllThreads(): List<TimelineEvent> {
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<TimelineEvent>): List<TimelineEvent> {
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
}
}
}

View file

@ -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<List<TimelineEvent>> {
return monarchy.findAllMappedWithChanges(
{ TimelineEventEntity.findAllLocalThreadNotificationsForRoomId(it, roomId = roomId) },
{ timelineEventMapper.map(it) }
)
}
override fun getNumberOfLocalThreadNotifications(): List<TimelineEvent> {
return monarchy.fetchAllMappedSync(
{ TimelineEventEntity.findAllLocalThreadNotificationsForRoomId(it, roomId = roomId) },
{ timelineEventMapper.map(it) }
)
}
override fun getAllThreadsLive(): LiveData<List<TimelineEvent>> {
return monarchy.findAllMappedWithChanges(
{ TimelineEventEntity.findAllThreadsForRoomId(it, roomId = roomId) },
{ timelineEventMapper.map(it) }
)
}
override fun getAllThreads(): List<TimelineEvent> {
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<TimelineEvent>): List<TimelineEvent> {
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
}
}
}