mirror of
https://github.com/element-hq/element-android
synced 2024-11-23 01:45:36 +03:00
Room : load all room members on first room opening
This commit is contained in:
parent
7ecbe09661
commit
2faba01662
16 changed files with 174 additions and 15 deletions
|
@ -43,6 +43,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventAdapter.Callback {
|
|||
super.onActivityCreated(savedInstanceState)
|
||||
setupRecyclerView()
|
||||
room = currentSession.getRoom(roomId)!!
|
||||
room.loadRoomMembersIfNeeded()
|
||||
room.liveTimeline().observe(this, Observer { renderEvents(it) })
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,12 @@ import im.vector.matrix.android.api.failure.Failure
|
|||
|
||||
interface MatrixCallback<in T> {
|
||||
|
||||
fun onSuccess(data: T)
|
||||
fun onSuccess(data: T) {
|
||||
//no-op
|
||||
}
|
||||
|
||||
fun onFailure(failure: Failure)
|
||||
fun onFailure(failure: Failure){
|
||||
//no-op
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ import android.arch.lifecycle.LiveData
|
|||
import android.arch.paging.PagedList
|
||||
import im.vector.matrix.android.api.session.events.model.EnrichedEvent
|
||||
import im.vector.matrix.android.api.session.room.model.MyMembership
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
|
||||
interface Room {
|
||||
|
||||
|
@ -15,4 +16,5 @@ interface Room {
|
|||
|
||||
fun getNumberOfJoinedMembers(): Int
|
||||
|
||||
fun loadRoomMembersIfNeeded(): Cancelable
|
||||
}
|
|
@ -2,12 +2,21 @@ package im.vector.matrix.android.api.session.room.model
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
enum class Membership {
|
||||
enum class Membership(val value: String) {
|
||||
|
||||
@Json(name = "invite") INVITE,
|
||||
@Json(name = "join") JOIN,
|
||||
@Json(name = "knock") KNOCK,
|
||||
@Json(name = "leave") LEAVE,
|
||||
@Json(name = "ban") BAN;
|
||||
@Json(name = "invite")
|
||||
INVITE("invite"),
|
||||
|
||||
@Json(name = "join")
|
||||
JOIN("join"),
|
||||
|
||||
@Json(name = "knock")
|
||||
KNOCK("knock"),
|
||||
|
||||
@Json(name = "leave")
|
||||
LEAVE("leave"),
|
||||
|
||||
@Json(name = "ban")
|
||||
BAN("ban");
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package im.vector.matrix.android.api.util
|
||||
|
||||
interface Cancelable {
|
||||
fun cancel()
|
||||
}
|
||||
fun cancel() {
|
||||
//no-op
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ import io.realm.annotations.PrimaryKey
|
|||
import kotlin.properties.Delegates
|
||||
|
||||
open class RoomEntity(@PrimaryKey var roomId: String = "",
|
||||
var chunks: RealmList<ChunkEntity> = RealmList()
|
||||
var chunks: RealmList<ChunkEntity> = RealmList(),
|
||||
var areAllMembersLoaded: Boolean = false
|
||||
) : RealmObject() {
|
||||
|
||||
private var membershipStr: String = MyMembership.NONE.name
|
||||
|
|
|
@ -8,6 +8,11 @@ import io.realm.Realm
|
|||
import io.realm.RealmQuery
|
||||
import io.realm.Sort
|
||||
|
||||
fun EventEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<EventEntity> {
|
||||
return realm.where(EventEntity::class.java)
|
||||
.equalTo("eventId", eventId)
|
||||
}
|
||||
|
||||
fun EventEntity.Companion.where(realm: Realm, roomId: String, type: String? = null): RealmQuery<EventEntity> {
|
||||
val query = realm.where(EventEntity::class.java)
|
||||
.equalTo("chunk.room.roomId", roomId)
|
||||
|
|
|
@ -4,8 +4,8 @@ import com.zhuinden.monarchy.Monarchy
|
|||
import im.vector.matrix.android.api.session.room.RoomService
|
||||
import im.vector.matrix.android.internal.auth.data.SessionParams
|
||||
import im.vector.matrix.android.internal.session.room.DefaultRoomService
|
||||
import im.vector.matrix.android.internal.session.room.RoomDisplayNameResolver
|
||||
import im.vector.matrix.android.internal.session.room.RoomMemberDisplayNameResolver
|
||||
import im.vector.matrix.android.internal.session.room.members.RoomDisplayNameResolver
|
||||
import im.vector.matrix.android.internal.session.room.members.RoomMemberDisplayNameResolver
|
||||
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
||||
import io.realm.RealmConfiguration
|
||||
import org.koin.dsl.context.ModuleDefinition
|
||||
|
|
|
@ -4,17 +4,23 @@ import android.arch.lifecycle.LiveData
|
|||
import android.arch.paging.LivePagedListBuilder
|
||||
import android.arch.paging.PagedList
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor
|
||||
import im.vector.matrix.android.api.session.events.model.EnrichedEvent
|
||||
import im.vector.matrix.android.api.session.room.Room
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.MyMembership
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.events.interceptor.MessageEventInterceptor
|
||||
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersRequest
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback
|
||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||
import io.realm.Sort
|
||||
import org.koin.standalone.KoinComponent
|
||||
import org.koin.standalone.inject
|
||||
|
@ -26,7 +32,10 @@ data class DefaultRoom(
|
|||
) : Room, KoinComponent {
|
||||
|
||||
private val paginationRequest by inject<PaginationRequest>()
|
||||
private val loadRoomMembersRequest by inject<LoadRoomMembersRequest>()
|
||||
private val syncTokenStore by inject<SyncTokenStore>()
|
||||
private val monarchy by inject<Monarchy>()
|
||||
|
||||
private val boundaryCallback = TimelineBoundaryCallback(paginationRequest, roomId, monarchy, Executors.newSingleThreadExecutor())
|
||||
private val eventInterceptors = ArrayList<EnrichedEventInterceptor>()
|
||||
|
||||
|
@ -71,5 +80,22 @@ data class DefaultRoom(
|
|||
return roomSummary?.joinedMembersCount ?: 0
|
||||
}
|
||||
|
||||
override fun loadRoomMembersIfNeeded(): Cancelable {
|
||||
return if (areAllMembersLoaded()) {
|
||||
object : Cancelable {}
|
||||
} else {
|
||||
val token = syncTokenStore.getLastToken()
|
||||
loadRoomMembersRequest.execute(roomId, token, Membership.LEAVE, object : MatrixCallback<Boolean> {})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun areAllMembersLoaded(): Boolean {
|
||||
return monarchy
|
||||
.fetchAllCopiedSync { RoomEntity.where(it, roomId) }
|
||||
.firstOrNull()
|
||||
?.areAllMembersLoaded ?: false
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package im.vector.matrix.android.internal.session.room
|
||||
|
||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||
import im.vector.matrix.android.internal.session.room.model.RoomMembersResponse
|
||||
import im.vector.matrix.android.internal.session.room.model.TokenChunkEvent
|
||||
import kotlinx.coroutines.Deferred
|
||||
import retrofit2.Response
|
||||
|
@ -28,4 +29,20 @@ interface RoomAPI {
|
|||
): Deferred<Response<TokenChunkEvent>>
|
||||
|
||||
|
||||
/**
|
||||
* Get all members of a room
|
||||
*
|
||||
* @param roomId the room id where to get the members
|
||||
* @param syncToken the sync token (optional)
|
||||
* @param membership to include only one type of membership (optional)
|
||||
* @param notMembership to exclude one type of membership (optional)
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/members")
|
||||
fun getMembers(@Path("roomId") roomId: String,
|
||||
@Query("at") syncToken: String?,
|
||||
@Query("membership") membership: String?,
|
||||
@Query("not_membership") notMembership: String?
|
||||
): Deferred<Response<RoomMembersResponse>>
|
||||
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package im.vector.matrix.android.internal.session.room
|
||||
|
||||
import im.vector.matrix.android.internal.session.DefaultSession
|
||||
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersRequest
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest
|
||||
import org.koin.dsl.context.ModuleDefinition
|
||||
import org.koin.dsl.module.Module
|
||||
|
@ -20,5 +21,10 @@ class RoomModule : Module {
|
|||
scope(DefaultSession.SCOPE) {
|
||||
PaginationRequest(get(), get(), get(), get())
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
LoadRoomMembersRequest(get(), get(), get(), get())
|
||||
}
|
||||
|
||||
}.invoke()
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntity
|
|||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.last
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.room.members.RoomDisplayNameResolver
|
||||
import io.realm.Realm
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package im.vector.matrix.android.internal.session.room.members
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.flatMap
|
||||
import arrow.core.leftIfNull
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.query.findAllRoomMembers
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.room.RoomAPI
|
||||
import im.vector.matrix.android.internal.session.room.model.RoomMembersResponse
|
||||
import im.vector.matrix.android.internal.session.sync.StateEventsChunkHandler
|
||||
import im.vector.matrix.android.internal.util.CancelableCoroutine
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI,
|
||||
private val monarchy: Monarchy,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val stateEventsChunkHandler: StateEventsChunkHandler) {
|
||||
|
||||
fun execute(roomId: String,
|
||||
streamToken: String?,
|
||||
excludeMembership: Membership? = null,
|
||||
callback: MatrixCallback<Boolean>
|
||||
): Cancelable {
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
val responseOrFailure = execute(roomId, streamToken, excludeMembership)
|
||||
responseOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(true) })
|
||||
}
|
||||
return CancelableCoroutine(job)
|
||||
}
|
||||
|
||||
//TODO : manage stream token (we have 404 on some rooms actually)
|
||||
private suspend fun execute(roomId: String,
|
||||
streamToken: String?,
|
||||
excludeMembership: Membership?) = withContext(coroutineDispatchers.io) {
|
||||
|
||||
return@withContext executeRequest<RoomMembersResponse> {
|
||||
apiCall = roomAPI.getMembers(roomId, null, null, excludeMembership?.value)
|
||||
}.leftIfNull {
|
||||
Failure.Unknown(RuntimeException("RoomMembersResponse shouldn't be null"))
|
||||
}.flatMap { response ->
|
||||
try {
|
||||
insertInDb(response, roomId)
|
||||
Either.right(response)
|
||||
} catch (exception: Exception) {
|
||||
Either.Left(Failure.Unknown(exception))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertInDb(response: RoomMembersResponse, roomId: String) {
|
||||
monarchy.runTransactionSync { realm ->
|
||||
// We ignore all the already known members
|
||||
val roomMembers = EventEntity.findAllRoomMembers(realm, roomId)
|
||||
val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) }
|
||||
stateEventsChunkHandler.handle(realm, roomId, eventsToInsert)
|
||||
|
||||
RoomEntity
|
||||
.where(realm, roomId).findFirst()
|
||||
?.let { it.areAllMembersLoaded = true }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.room
|
||||
package im.vector.matrix.android.internal.session.room.members
|
||||
|
||||
import android.content.Context
|
||||
import com.zhuinden.monarchy.Monarchy
|
|
@ -1,4 +1,4 @@
|
|||
package im.vector.matrix.android.internal.session.room
|
||||
package im.vector.matrix.android.internal.session.room.members
|
||||
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package im.vector.matrix.android.internal.session.room.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomMembersResponse(
|
||||
@Json(name = "chunk") val roomMemberEvents: List<Event>
|
||||
)
|
Loading…
Reference in a new issue