Start handling room alias

This commit is contained in:
ganfra 2019-11-21 20:38:01 +01:00 committed by Benoit Marty
parent 91c98d4bfb
commit 02febfb01b
13 changed files with 165 additions and 12 deletions

View file

@ -24,9 +24,9 @@ import android.net.Uri
*/
sealed class PermalinkData {
data class EventLink(val roomIdOrAlias: String, val eventId: String) : PermalinkData()
data class EventLink(val roomIdOrAlias: String, val eventId: String, val isRoomAlias: Boolean) : PermalinkData()
data class RoomLink(val roomIdOrAlias: String) : PermalinkData()
data class RoomLink(val roomIdOrAlias: String, val isRoomAlias: Boolean) : PermalinkData()
data class UserLink(val userId: String) : PermalinkData()

View file

@ -18,6 +18,8 @@ package im.vector.matrix.android.api.permalinks
import android.net.Uri
import im.vector.matrix.android.api.MatrixPatterns
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* This class turns an uri to a [PermalinkData]
@ -60,16 +62,23 @@ object PermalinkParser {
return PermalinkData.FallbackLink(uri)
}
return when {
MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
MatrixPatterns.isRoomId(identifier) -> {
MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
MatrixPatterns.isRoomId(identifier) -> {
if (!extraParameter.isNullOrEmpty() && MatrixPatterns.isEventId(extraParameter)) {
PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter)
PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter, isRoomAlias = false)
} else {
PermalinkData.RoomLink(roomIdOrAlias = identifier)
PermalinkData.RoomLink(roomIdOrAlias = identifier, isRoomAlias = false)
}
}
else -> PermalinkData.FallbackLink(uri)
MatrixPatterns.isRoomAlias(identifier) -> {
if (!extraParameter.isNullOrEmpty() && MatrixPatterns.isEventId(extraParameter)) {
PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter, isRoomAlias = true)
} else {
PermalinkData.RoomLink(roomIdOrAlias = identifier, isRoomAlias = true)
}
}
else -> PermalinkData.FallbackLink(uri)
}
}
}

View file

@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.room
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.util.Cancelable
@ -74,4 +75,10 @@ interface RoomService {
*/
fun markAllAsRead(roomIds: List<String>,
callback: MatrixCallback<Unit>): Cancelable
/**
* Resolve a room alias to a room ID.
*/
fun getRoomIdByAlias(roomAlias: String,
callback: MatrixCallback<String?>): Cancelable
}

View file

@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session.room.model
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
import im.vector.matrix.android.api.session.room.send.UserDraft
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import io.realm.RealmList
/**
* This class holds some data of a room.
@ -29,6 +30,8 @@ data class RoomSummary(
val displayName: String = "",
val topic: String = "",
val avatarUrl: String = "",
val canonicalAlias: String? = null,
val aliases: List<String> = emptyList(),
val isDirect: Boolean = false,
val latestPreviewableEvent: TimelineEvent? = null,
val otherMemberIds: List<String> = emptyList(),

View file

@ -68,7 +68,9 @@ internal class RoomSummaryMapper @Inject constructor(
membership = roomSummaryEntity.membership,
versioningState = roomSummaryEntity.versioningState,
readMarkerId = roomSummaryEntity.readMarkerId,
userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) } ?: emptyList()
userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) } ?: emptyList(),
canonicalAlias = roomSummaryEntity.canonicalAlias,
aliases = roomSummaryEntity.aliases.toList()
)
}
}

View file

@ -40,6 +40,8 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
var tags: RealmList<RoomTagEntity> = RealmList(),
var userDrafts: UserDraftsEntity? = null,
var breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS
var canonicalAlias: String? = null,
var aliases: RealmList<String> = RealmList()
) : RealmObject() {
private var membershipStr: String = Membership.NONE.name

View file

@ -32,6 +32,18 @@ internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = n
return query
}
internal fun RoomSummaryEntity.Companion.findByAlias(realm: Realm, roomAlias: String): RoomSummaryEntity? {
val roomSummary = realm.where<RoomSummaryEntity>()
.equalTo(RoomSummaryEntityFields.CANONICAL_ALIAS, roomAlias)
.findFirst()
if (roomSummary != null) {
return roomSummary
}
return realm.where<RoomSummaryEntity>()
.`in`(RoomSummaryEntityFields.ALIASES.`$`, arrayOf(roomAlias))
.findFirst()
}
internal fun RoomSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String): RoomSummaryEntity {
return where(realm, roomId).findFirst() ?: realm.createObject(roomId)
}

View file

@ -21,6 +21,7 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.VersioningState
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
@ -30,6 +31,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.model.RoomSummaryEntityFields
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask
import im.vector.matrix.android.internal.session.room.create.CreateRoomTask
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask
@ -45,9 +47,11 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
private val joinRoomTask: JoinRoomTask,
private val markAllRoomsReadTask: MarkAllRoomsReadTask,
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
private val roomIdByAliasTask: GetRoomIdByAliasTask,
private val roomFactory: RoomFactory,
private val taskExecutor: TaskExecutor) : RoomService {
override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback<String>): Cancelable {
return createRoomTask
.configureWith(createRoomParams) {
@ -111,4 +115,12 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
}
.executeBy(taskExecutor)
}
override fun getRoomIdByAlias(roomAlias: String, callback: MatrixCallback<String?>): Cancelable {
return roomIdByAliasTask
.configureWith(GetRoomIdByAliasTask.Params(roomAlias)) {
this.callback = callback
}
.executeBy(taskExecutor)
}
}

View file

@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.room
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
@ -258,4 +259,12 @@ internal interface RoomAPI {
fun reportContent(@Path("roomId") roomId: String,
@Path("eventId") eventId: String,
@Body body: ReportContentBody): Call<Unit>
/**
* Get the room ID associated to the room alias.
*
* @param roomAlias the room alias.
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call<RoomAliasDescription>
}

View file

@ -24,6 +24,8 @@ import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.internal.session.DefaultFileService
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.room.alias.DefaultGetRoomIdByAliasTask
import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask
import im.vector.matrix.android.internal.session.room.create.CreateRoomTask
import im.vector.matrix.android.internal.session.room.create.DefaultCreateRoomTask
import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask
@ -133,4 +135,7 @@ internal abstract class RoomModule {
@Binds
abstract fun bindFetchEditHistoryTask(fetchEditHistoryTask: DefaultFetchEditHistoryTask): FetchEditHistoryTask
@Binds
abstract fun bindGetRoomIdByAliasTask(getRoomIdByAliasTask: DefaultGetRoomIdByAliasTask): GetRoomIdByAliasTask
}

View file

@ -20,6 +20,8 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.model.EventEntity
@ -68,7 +70,7 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
unreadNotifications: RoomSyncUnreadNotifications? = null,
updateMembers: Boolean = false) {
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId)
?: realm.createObject(roomId)
if (roomSummary != null) {
if (roomSummary.heroes.isNotEmpty()) {
@ -91,15 +93,24 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES)
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()
val lastCanonicalAliasEvent = EventEntity.where(realm, roomId, EventType.STATE_CANONICAL_ALIAS).prev()
val lastAliasesEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
// avoid this call if we are sure there are unread events
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
// avoid this call if we are sure there are unread events
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel<RoomTopicContent>()?.topic
roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent
roomSummaryEntity.canonicalAlias = ContentMapper.map(lastCanonicalAliasEvent?.content).toModel<RoomCanonicalAliasContent>()
?.canonicalAlias
val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel<RoomAliasesContent>()?.aliases ?: emptyList()
roomSummaryEntity.aliases.clear()
roomSummaryEntity.aliases.addAll(roomAliases)
if (updateMembers) {
val otherRoomMembers = RoomMembers(realm, roomId)

View file

@ -0,0 +1,48 @@
/*
* Copyright 2019 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.session.room.alias
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.findByAlias
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.task.Task
import io.realm.Realm
import javax.inject.Inject
internal interface GetRoomIdByAliasTask : Task<GetRoomIdByAliasTask.Params, String?> {
data class Params(
val roomAlias: String
)
}
internal class DefaultGetRoomIdByAliasTask @Inject constructor(private val monarchy: Monarchy,
private val roomAPI: RoomAPI) : GetRoomIdByAliasTask {
override suspend fun execute(params: GetRoomIdByAliasTask.Params): String? {
val roomId = Realm.getInstance(monarchy.realmConfiguration).use {
RoomSummaryEntity.findByAlias(it, params.roomAlias)?.roomId
}
if (roomId != null) {
return roomId
}
return executeRequest<RoomAliasDescription> {
apiCall = roomAPI.getRoomIdByAlias(params.roomAlias)
}.roomId
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2019 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.session.room.alias
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class RoomAliasDescription(
/**
* The room ID for this alias.
*/
@Json(name = "room_id") val roomId: String,
/**
* A list of servers that are aware of this room ID.
*/
@Json(name = "servers") val servers: List<String> = emptyList()
)