diff --git a/app/src/main/java/im/vector/riotredesign/features/home/RoomDetailFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/RoomDetailFragment.kt index 892b741ed1..18ef510c5d 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/RoomDetailFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/RoomDetailFragment.kt @@ -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) }) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt index 5106a4cb11..db85de5ccf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt @@ -4,8 +4,12 @@ import im.vector.matrix.android.api.failure.Failure interface MatrixCallback { - fun onSuccess(data: T) + fun onSuccess(data: T) { + //no-op + } - fun onFailure(failure: Failure) + fun onFailure(failure: Failure){ + //no-op + } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt index a9055c4d57..febe786c91 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt @@ -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 } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/Membership.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/Membership.kt index 8bb57b15c5..81cdd00d1b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/Membership.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/Membership.kt @@ -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"); } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Cancelable.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Cancelable.kt index e8092d7271..c83dc88b4c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Cancelable.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Cancelable.kt @@ -1,5 +1,8 @@ package im.vector.matrix.android.api.util interface Cancelable { - fun cancel() -} \ No newline at end of file + fun cancel() { + //no-op + } +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt index 44daadeda7..8aa3251dfd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt @@ -8,7 +8,8 @@ import io.realm.annotations.PrimaryKey import kotlin.properties.Delegates open class RoomEntity(@PrimaryKey var roomId: String = "", - var chunks: RealmList = RealmList() + var chunks: RealmList = RealmList(), + var areAllMembersLoaded: Boolean = false ) : RealmObject() { private var membershipStr: String = MyMembership.NONE.name diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index 8abba422d6..b18b67ac25 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -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 { + return realm.where(EventEntity::class.java) + .equalTo("eventId", eventId) +} + fun EventEntity.Companion.where(realm: Realm, roomId: String, type: String? = null): RealmQuery { val query = realm.where(EventEntity::class.java) .equalTo("chunk.room.roomId", roomId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index ea2085eb3a..490f9e54ee 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt index fb63b721aa..d01eba5f64 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt @@ -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() + private val loadRoomMembersRequest by inject() + private val syncTokenStore by inject() private val monarchy by inject() + private val boundaryCallback = TimelineBoundaryCallback(paginationRequest, roomId, monarchy, Executors.newSingleThreadExecutor()) private val eventInterceptors = ArrayList() @@ -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 {}) + } + } + + + private fun areAllMembersLoaded(): Boolean { + return monarchy + .fetchAllCopiedSync { RoomEntity.where(it, roomId) } + .firstOrNull() + ?.areAllMembersLoaded ?: false + } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt index cf2e9a61ee..a93e310746 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt @@ -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> + /** + * 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> + + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index 45bc843837..0aee01b8d4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -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() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index 6c1aff68b7..302d255403 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt new file mode 100644 index 0000000000..ca5613fd20 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt @@ -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 + ): 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 { + 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 } + } + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomDisplayNameResolver.kt similarity index 98% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomDisplayNameResolver.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomDisplayNameResolver.kt index 11ec0f1add..8a1ec1cafe 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomDisplayNameResolver.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomMemberDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMemberDisplayNameResolver.kt similarity index 95% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomMemberDisplayNameResolver.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMemberDisplayNameResolver.kt index 71d7da554d..9da9400cdc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomMemberDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMemberDisplayNameResolver.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/model/RoomMembersResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/model/RoomMembersResponse.kt new file mode 100644 index 0000000000..6143a35e40 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/model/RoomMembersResponse.kt @@ -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 +) \ No newline at end of file