mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 05:31:21 +03:00
Tombstone : handle joining viaserver params
This commit is contained in:
parent
9a1e16a170
commit
ac38a6461c
28 changed files with 293 additions and 82 deletions
|
@ -123,9 +123,9 @@ object MatrixPatterns {
|
||||||
*/
|
*/
|
||||||
fun isEventId(str: String?): Boolean {
|
fun isEventId(str: String?): Boolean {
|
||||||
return str != null
|
return str != null
|
||||||
&& (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER
|
&& (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER
|
||||||
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3
|
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3
|
||||||
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
|
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,4 +137,23 @@ object MatrixPatterns {
|
||||||
fun isGroupId(str: String?): Boolean {
|
fun isGroupId(str: String?): Boolean {
|
||||||
return str != null && str matches PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
|
return str != null && str matches PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract server name from a matrix id
|
||||||
|
*
|
||||||
|
* @param matrixId
|
||||||
|
* @return null if not found or if matrixId is null
|
||||||
|
*/
|
||||||
|
fun extractServerNameFromId(matrixId: String?): String? {
|
||||||
|
if (matrixId == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val index = matrixId.lastIndexOf(":")
|
||||||
|
|
||||||
|
return if (index == -1) {
|
||||||
|
null
|
||||||
|
} else matrixId.substring(index + 1)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,11 +34,6 @@ interface RoomDirectoryService {
|
||||||
publicRoomsParams: PublicRoomsParams,
|
publicRoomsParams: PublicRoomsParams,
|
||||||
callback: MatrixCallback<PublicRoomsResponse>): Cancelable
|
callback: MatrixCallback<PublicRoomsResponse>): Cancelable
|
||||||
|
|
||||||
/**
|
|
||||||
* Join a room by id
|
|
||||||
*/
|
|
||||||
fun joinRoom(roomId: String,
|
|
||||||
callback: MatrixCallback<Unit>)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the overall metadata about protocols supported by the homeserver.
|
* Fetches the overall metadata about protocols supported by the homeserver.
|
||||||
|
|
|
@ -32,6 +32,15 @@ interface RoomService {
|
||||||
fun createRoom(createRoomParams: CreateRoomParams,
|
fun createRoom(createRoomParams: CreateRoomParams,
|
||||||
callback: MatrixCallback<String>)
|
callback: MatrixCallback<String>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join a room by id
|
||||||
|
* @param roomId the roomId of the room to join
|
||||||
|
* @param viaServers the servers to attempt to join the room through. One of the servers must be participating in the room.
|
||||||
|
*/
|
||||||
|
fun joinRoom(roomId: String,
|
||||||
|
viaServers: List<String> = emptyList(),
|
||||||
|
callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a room from a roomId
|
* Get a room from a roomId
|
||||||
* @param roomId the roomId to look for.
|
* @param roomId the roomId to look for.
|
||||||
|
|
|
@ -57,7 +57,7 @@ interface MembershipService {
|
||||||
/**
|
/**
|
||||||
* Join the room, or accept an invitation.
|
* Join the room, or accept an invitation.
|
||||||
*/
|
*/
|
||||||
fun join(callback: MatrixCallback<Unit>)
|
fun join(viaServers: List<String> = emptyList(), callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leave the room, or reject an invitation.
|
* Leave the room, or reject an invitation.
|
||||||
|
|
|
@ -35,5 +35,9 @@ data class RoomSummary(
|
||||||
val highlightCount: Int = 0,
|
val highlightCount: Int = 0,
|
||||||
val tags: List<RoomTag> = emptyList(),
|
val tags: List<RoomTag> = emptyList(),
|
||||||
val membership: Membership = Membership.NONE,
|
val membership: Membership = Membership.NONE,
|
||||||
val isVersioned: Boolean = false
|
val versioningState: VersioningState = VersioningState.NONE
|
||||||
)
|
) {
|
||||||
|
|
||||||
|
val isVersioned: Boolean
|
||||||
|
get() = versioningState != VersioningState.NONE
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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.api.session.room.model
|
||||||
|
|
||||||
|
enum class VersioningState {
|
||||||
|
NONE,
|
||||||
|
UPGRADED_ROOM_NOT_JOINED,
|
||||||
|
UPGRADED_ROOM_JOINED
|
||||||
|
}
|
|
@ -62,7 +62,7 @@ internal class RoomSummaryMapper @Inject constructor(
|
||||||
notificationCount = roomSummaryEntity.notificationCount,
|
notificationCount = roomSummaryEntity.notificationCount,
|
||||||
tags = tags,
|
tags = tags,
|
||||||
membership = roomSummaryEntity.membership,
|
membership = roomSummaryEntity.membership,
|
||||||
isVersioned = roomSummaryEntity.isVersioned
|
versioningState = roomSummaryEntity.versioningState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.matrix.android.internal.database.model
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
|
import im.vector.matrix.android.api.session.room.model.VersioningState
|
||||||
import io.realm.RealmList
|
import io.realm.RealmList
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.Ignore
|
import io.realm.annotations.Ignore
|
||||||
|
@ -35,17 +36,23 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
||||||
var otherMemberIds: RealmList<String> = RealmList(),
|
var otherMemberIds: RealmList<String> = RealmList(),
|
||||||
var notificationCount: Int = 0,
|
var notificationCount: Int = 0,
|
||||||
var highlightCount: Int = 0,
|
var highlightCount: Int = 0,
|
||||||
var tags: RealmList<RoomTagEntity> = RealmList(),
|
var tags: RealmList<RoomTagEntity> = RealmList()
|
||||||
var isVersioned: Boolean = false
|
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
private var membershipStr: String = Membership.NONE.name
|
private var membershipStr: String = Membership.NONE.name
|
||||||
|
private var versioningStateStr: String = VersioningState.NONE.name
|
||||||
|
|
||||||
|
|
||||||
@delegate:Ignore
|
@delegate:Ignore
|
||||||
var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue ->
|
var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue ->
|
||||||
membershipStr = newValue.name
|
membershipStr = newValue.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@delegate:Ignore
|
||||||
|
var versioningState: VersioningState by Delegates.observable(VersioningState.valueOf(versioningStateStr)) { _, _, newValue ->
|
||||||
|
versioningStateStr = newValue.name
|
||||||
|
}
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
|
|
||||||
}
|
}
|
|
@ -37,6 +37,7 @@ import im.vector.matrix.android.internal.network.AccessTokenInterceptor
|
||||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||||
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
||||||
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
||||||
|
import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver
|
||||||
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
||||||
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
||||||
import im.vector.matrix.android.internal.util.md5
|
import im.vector.matrix.android.internal.util.md5
|
||||||
|
@ -131,8 +132,11 @@ internal abstract class SessionModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindRoomCreateEventLiveObserver(roomTombstoneEventLiveObserver: RoomTombstoneEventLiveObserver): LiveEntityObserver
|
abstract fun bindRoomTombstoneEventLiveObserver(roomTombstoneEventLiveObserver: RoomTombstoneEventLiveObserver): LiveEntityObserver
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun bindRoomCreateEventLiveObserver(roomCreateEventLiveObserver: RoomCreateEventLiveObserver): LiveEntityObserver
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
||||||
|
|
|
@ -31,7 +31,6 @@ import im.vector.matrix.android.internal.task.toConfigurableTask
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask,
|
internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask,
|
||||||
private val joinRoomTask: JoinRoomTask,
|
|
||||||
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
|
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
|
||||||
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
|
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
|
||||||
|
|
||||||
|
@ -44,13 +43,6 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun joinRoom(roomId: String, callback: MatrixCallback<Unit>) {
|
|
||||||
joinRoomTask
|
|
||||||
.configureWith(JoinRoomTask.Params(roomId))
|
|
||||||
.dispatchTo(callback)
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>) {
|
override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>) {
|
||||||
getThirdPartyProtocolsTask
|
getThirdPartyProtocolsTask
|
||||||
.toConfigurableTask()
|
.toConfigurableTask()
|
||||||
|
|
|
@ -22,6 +22,7 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.room.Room
|
import im.vector.matrix.android.api.session.room.Room
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
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
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
|
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
|
@ -29,6 +30,7 @@ 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.model.RoomSummaryEntityFields
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.session.room.create.CreateRoomTask
|
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.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
import im.vector.matrix.android.internal.util.fetchManaged
|
import im.vector.matrix.android.internal.util.fetchManaged
|
||||||
|
@ -37,6 +39,7 @@ import javax.inject.Inject
|
||||||
internal class DefaultRoomService @Inject constructor(private val monarchy: Monarchy,
|
internal class DefaultRoomService @Inject constructor(private val monarchy: Monarchy,
|
||||||
private val roomSummaryMapper: RoomSummaryMapper,
|
private val roomSummaryMapper: RoomSummaryMapper,
|
||||||
private val createRoomTask: CreateRoomTask,
|
private val createRoomTask: CreateRoomTask,
|
||||||
|
private val joinRoomTask: JoinRoomTask,
|
||||||
private val roomFactory: RoomFactory,
|
private val roomFactory: RoomFactory,
|
||||||
private val taskExecutor: TaskExecutor) : RoomService {
|
private val taskExecutor: TaskExecutor) : RoomService {
|
||||||
|
|
||||||
|
@ -57,9 +60,16 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
|
||||||
{ realm ->
|
{ realm ->
|
||||||
RoomSummaryEntity.where(realm)
|
RoomSummaryEntity.where(realm)
|
||||||
.isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
|
.isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
|
||||||
.notEqualTo(RoomSummaryEntityFields.IS_VERSIONED, true)
|
.notEqualTo(RoomSummaryEntityFields.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name)
|
||||||
},
|
},
|
||||||
{ roomSummaryMapper.map(it) }
|
{ roomSummaryMapper.map(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun joinRoom(roomId: String, viaServers: List<String>, callback: MatrixCallback<Unit>) {
|
||||||
|
joinRoomTask
|
||||||
|
.configureWith(JoinRoomTask.Params(roomId, viaServers))
|
||||||
|
.dispatchTo(callback)
|
||||||
|
.executeBy(taskExecutor)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -218,6 +218,7 @@ internal interface RoomAPI {
|
||||||
*/
|
*/
|
||||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/join")
|
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/join")
|
||||||
fun join(@Path("roomId") roomId: String,
|
fun join(@Path("roomId") roomId: String,
|
||||||
|
@Query("server_name") viaServers: List<String>,
|
||||||
@Body params: Map<String, String>): Call<Unit>
|
@Body params: Map<String, String>): Call<Unit>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* 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.create
|
||||||
|
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
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.VersioningState
|
||||||
|
import im.vector.matrix.android.api.session.room.model.create.RoomCreateContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
||||||
|
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
||||||
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
|
import im.vector.matrix.android.internal.database.query.types
|
||||||
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
|
import io.realm.OrderedCollectionChangeSet
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import io.realm.RealmResults
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class RoomCreateEventLiveObserver @Inject constructor(@SessionDatabase
|
||||||
|
realmConfiguration: RealmConfiguration)
|
||||||
|
: RealmLiveEntityObserver<EventEntity>(realmConfiguration) {
|
||||||
|
|
||||||
|
override val query = Monarchy.Query<EventEntity> {
|
||||||
|
EventEntity.types(it, listOf(EventType.STATE_ROOM_CREATE))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onChange(results: RealmResults<EventEntity>, changeSet: OrderedCollectionChangeSet) {
|
||||||
|
changeSet.insertions
|
||||||
|
.asSequence()
|
||||||
|
.mapNotNull {
|
||||||
|
results[it]?.asDomain()
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
.also {
|
||||||
|
handleRoomCreateEvents(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRoomCreateEvents(createEvents: List<Event>) = Realm.getInstance(realmConfiguration).use {
|
||||||
|
it.executeTransactionAsync { realm ->
|
||||||
|
for (event in createEvents) {
|
||||||
|
val createRoomContent = event.getClearContent().toModel<RoomCreateContent>()
|
||||||
|
val predecessorRoomId = createRoomContent?.predecessor?.roomId ?: continue
|
||||||
|
|
||||||
|
val predecessorRoomSummary = RoomSummaryEntity.where(realm, predecessorRoomId).findFirst()
|
||||||
|
?: RoomSummaryEntity(predecessorRoomId)
|
||||||
|
predecessorRoomSummary.versioningState = VersioningState.UPGRADED_ROOM_JOINED
|
||||||
|
realm.insertOrUpdate(predecessorRoomSummary)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -80,8 +80,8 @@ internal class DefaultMembershipService @Inject constructor(private val roomId:
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun join(callback: MatrixCallback<Unit>) {
|
override fun join(viaServers: List<String>, callback: MatrixCallback<Unit>) {
|
||||||
val params = JoinRoomTask.Params(roomId)
|
val params = JoinRoomTask.Params(roomId, viaServers)
|
||||||
joinTask.configureWith(params)
|
joinTask.configureWith(params)
|
||||||
.dispatchTo(callback)
|
.dispatchTo(callback)
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
|
|
|
@ -25,7 +25,8 @@ import javax.inject.Inject
|
||||||
|
|
||||||
internal interface JoinRoomTask : Task<JoinRoomTask.Params, Unit> {
|
internal interface JoinRoomTask : Task<JoinRoomTask.Params, Unit> {
|
||||||
data class Params(
|
data class Params(
|
||||||
val roomId: String
|
val roomId: String,
|
||||||
|
val viaServers: List<String> = emptyList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ internal class DefaultJoinRoomTask @Inject constructor(private val roomAPI: Room
|
||||||
|
|
||||||
override suspend fun execute(params: JoinRoomTask.Params): Try<Unit> {
|
override suspend fun execute(params: JoinRoomTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomAPI.join(params.roomId, HashMap())
|
apiCall = roomAPI.join(params.roomId, params.viaServers, HashMap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
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.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.model.VersioningState
|
||||||
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
|
@ -64,8 +65,10 @@ internal class RoomTombstoneEventLiveObserver @Inject constructor(@SessionDataba
|
||||||
if (createRoomContent?.replacementRoom == null) continue
|
if (createRoomContent?.replacementRoom == null) continue
|
||||||
|
|
||||||
val predecessorRoomSummary = RoomSummaryEntity.where(realm, event.roomId).findFirst()
|
val predecessorRoomSummary = RoomSummaryEntity.where(realm, event.roomId).findFirst()
|
||||||
?: RoomSummaryEntity(event.roomId)
|
?: RoomSummaryEntity(event.roomId)
|
||||||
predecessorRoomSummary.isVersioned = true
|
if (predecessorRoomSummary.versioningState == VersioningState.NONE) {
|
||||||
|
predecessorRoomSummary.versioningState = VersioningState.UPGRADED_ROOM_NOT_JOINED
|
||||||
|
}
|
||||||
realm.insertOrUpdate(predecessorRoomSummary)
|
realm.insertOrUpdate(predecessorRoomSummary)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,17 +29,16 @@ import android.widget.ImageView
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.text.bold
|
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
import butterknife.ButterKnife
|
||||||
import im.vector.matrix.android.api.failure.MatrixError
|
import im.vector.matrix.android.api.failure.MatrixError
|
||||||
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
|
|
||||||
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.error.ResourceLimitErrorFormatter
|
import im.vector.riotx.core.error.ResourceLimitErrorFormatter
|
||||||
import im.vector.riotx.features.themes.ThemeUtils
|
import im.vector.riotx.features.themes.ThemeUtils
|
||||||
import me.gujun.android.span.addSpan
|
|
||||||
import me.gujun.android.span.span
|
import me.gujun.android.span.span
|
||||||
import me.saket.bettermovementmethod.BetterLinkMovementMethod
|
import me.saket.bettermovementmethod.BetterLinkMovementMethod
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -108,19 +107,14 @@ class NotificationAreaView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderTombstone(state: State.Tombstone) {
|
private fun renderTombstone(state: State.Tombstone) {
|
||||||
val roomTombstoneContent = state.tombstoneContent
|
|
||||||
val roomLink = PermalinkFactory.createPermalink(roomTombstoneContent.replacementRoom)
|
|
||||||
?: return
|
|
||||||
|
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
imageView.setImageResource(R.drawable.error)
|
imageView.setImageResource(R.drawable.error)
|
||||||
val textColorInt = ThemeUtils.getColor(context, R.attr.vctr_message_text_color)
|
|
||||||
val message = span {
|
val message = span {
|
||||||
+resources.getString(R.string.room_tombstone_versioned_description)
|
+resources.getString(R.string.room_tombstone_versioned_description)
|
||||||
+"\n"
|
+"\n"
|
||||||
span(resources.getString(R.string.room_tombstone_continuation_link)) {
|
span(resources.getString(R.string.room_tombstone_continuation_link)) {
|
||||||
textDecorationLine = "underline"
|
textDecorationLine = "underline"
|
||||||
onClick = { delegate?.onUrlClicked(roomLink) }
|
onClick = { delegate?.onTombstoneEventClicked(state.tombstoneEvent) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
messageView.movementMethod = BetterLinkMovementMethod.getInstance()
|
messageView.movementMethod = BetterLinkMovementMethod.getInstance()
|
||||||
|
@ -274,7 +268,7 @@ class NotificationAreaView @JvmOverloads constructor(
|
||||||
object ConnectionError : State()
|
object ConnectionError : State()
|
||||||
|
|
||||||
// The room is dead
|
// The room is dead
|
||||||
data class Tombstone(val tombstoneContent: RoomTombstoneContent) : State()
|
data class Tombstone(val tombstoneEvent: Event) : State()
|
||||||
|
|
||||||
// Somebody is typing
|
// Somebody is typing
|
||||||
data class Typing(val message: String) : State()
|
data class Typing(val message: String) : State()
|
||||||
|
@ -293,7 +287,7 @@ class NotificationAreaView @JvmOverloads constructor(
|
||||||
* An interface to delegate some actions to another object
|
* An interface to delegate some actions to another object
|
||||||
*/
|
*/
|
||||||
interface Delegate {
|
interface Delegate {
|
||||||
fun onUrlClicked(url: String)
|
fun onTombstoneEventClicked(tombstoneEvent: Event)
|
||||||
fun resendUnsentEvents()
|
fun resendUnsentEvents()
|
||||||
fun deleteUnsentEvents()
|
fun deleteUnsentEvents()
|
||||||
fun closeScreen()
|
fun closeScreen()
|
||||||
|
|
|
@ -145,7 +145,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
|
|
||||||
if (intent?.hasExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION) == true) {
|
if (intent?.hasExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION) == true) {
|
||||||
notificationDrawerManager.clearAllEvents()
|
notificationDrawerManager.clearAllEvents()
|
||||||
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
|
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
|
||||||
|
@ -194,7 +193,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
bugReporter.openBugReportScreen(this, false)
|
bugReporter.openBugReportScreen(this, false)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.menu_home_filter -> {
|
R.id.menu_home_filter -> {
|
||||||
navigator.openRoomsFiltering(this)
|
navigator.openRoomsFiltering(this)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.riotx.features.home.room.detail
|
package im.vector.riotx.features.home.room.detail
|
||||||
|
|
||||||
import com.jaiselrahman.filepicker.model.MediaFile
|
import com.jaiselrahman.filepicker.model.MediaFile
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
|
@ -27,13 +28,14 @@ sealed class RoomDetailActions {
|
||||||
data class SendMessage(val text: String, val autoMarkdown: Boolean) : RoomDetailActions()
|
data class SendMessage(val text: String, val autoMarkdown: Boolean) : RoomDetailActions()
|
||||||
data class SendMedia(val mediaFiles: List<MediaFile>) : RoomDetailActions()
|
data class SendMedia(val mediaFiles: List<MediaFile>) : RoomDetailActions()
|
||||||
data class EventDisplayed(val event: TimelineEvent) : RoomDetailActions()
|
data class EventDisplayed(val event: TimelineEvent) : RoomDetailActions()
|
||||||
data class LoadMore(val direction: Timeline.Direction) : RoomDetailActions()
|
data class LoadMoreTimelineEvents(val direction: Timeline.Direction) : RoomDetailActions()
|
||||||
data class SendReaction(val reaction: String, val targetEventId: String) : RoomDetailActions()
|
data class SendReaction(val reaction: String, val targetEventId: String) : RoomDetailActions()
|
||||||
data class RedactAction(val targetEventId: String, val reason: String? = "") : RoomDetailActions()
|
data class RedactAction(val targetEventId: String, val reason: String? = "") : RoomDetailActions()
|
||||||
data class UndoReaction(val targetEventId: String, val key: String, val reason: String? = "") : RoomDetailActions()
|
data class UndoReaction(val targetEventId: String, val key: String, val reason: String? = "") : RoomDetailActions()
|
||||||
data class UpdateQuickReactAction(val targetEventId: String, val selectedReaction: String, val add: Boolean) : RoomDetailActions()
|
data class UpdateQuickReactAction(val targetEventId: String, val selectedReaction: String, val add: Boolean) : RoomDetailActions()
|
||||||
data class NavigateToEvent(val eventId: String, val position: Int?) : RoomDetailActions()
|
data class NavigateToEvent(val eventId: String, val position: Int?) : RoomDetailActions()
|
||||||
data class DownloadFile(val eventId: String, val messageFileContent: MessageFileContent) : RoomDetailActions()
|
data class DownloadFile(val eventId: String, val messageFileContent: MessageFileContent) : RoomDetailActions()
|
||||||
|
data class HandleTombstoneEvent(val event: Event): RoomDetailActions()
|
||||||
object AcceptInvite : RoomDetailActions()
|
object AcceptInvite : RoomDetailActions()
|
||||||
object RejectInvite : RoomDetailActions()
|
object RejectInvite : RoomDetailActions()
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.replaceFragment
|
import im.vector.riotx.core.extensions.replaceFragment
|
||||||
import im.vector.riotx.core.platform.ToolbarConfigurable
|
import im.vector.riotx.core.platform.ToolbarConfigurable
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
|
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
||||||
|
|
||||||
class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
|
class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
waitingView = waiting_view
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
val roomDetailArgs: RoomDetailArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
|
val roomDetailArgs: RoomDetailArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
|
||||||
?: return
|
?: return
|
||||||
|
|
|
@ -45,6 +45,12 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
||||||
|
import com.airbnb.mvrx.Async
|
||||||
|
import com.airbnb.mvrx.DeliveryMode
|
||||||
|
import com.airbnb.mvrx.Fail
|
||||||
|
import com.airbnb.mvrx.Loading
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.github.piasy.biv.BigImageViewer
|
import com.github.piasy.biv.BigImageViewer
|
||||||
|
@ -58,6 +64,7 @@ import com.otaliastudios.autocomplete.AutocompleteCallback
|
||||||
import com.otaliastudios.autocomplete.CharPolicy
|
import com.otaliastudios.autocomplete.CharPolicy
|
||||||
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.api.session.room.model.message.*
|
import im.vector.matrix.android.api.session.room.model.message.*
|
||||||
|
@ -73,6 +80,8 @@ import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
import im.vector.riotx.core.extensions.hideKeyboard
|
import im.vector.riotx.core.extensions.hideKeyboard
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
import im.vector.riotx.core.extensions.observeEvent
|
||||||
|
import im.vector.riotx.core.extensions.observeK
|
||||||
|
import im.vector.riotx.core.extensions.observeNotNull
|
||||||
import im.vector.riotx.core.extensions.setTextOrHide
|
import im.vector.riotx.core.extensions.setTextOrHide
|
||||||
import im.vector.riotx.core.files.addEntryToDownloadManager
|
import im.vector.riotx.core.files.addEntryToDownloadManager
|
||||||
import im.vector.riotx.core.glide.GlideApp
|
import im.vector.riotx.core.glide.GlideApp
|
||||||
|
@ -108,7 +117,9 @@ import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import im.vector.riotx.features.themes.ThemeUtils
|
import im.vector.riotx.features.themes.ThemeUtils
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
||||||
|
import kotlinx.android.synthetic.main.item_loading.*
|
||||||
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
|
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
|
||||||
|
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
||||||
import org.commonmark.parser.Parser
|
import org.commonmark.parser.Parser
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -222,6 +233,10 @@ class RoomDetailFragment :
|
||||||
scrollOnHighlightedEventCallback.scheduleScrollTo(it)
|
scrollOnHighlightedEventCallback.scheduleScrollTo(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
roomDetailViewModel.selectSubscribe(this, RoomDetailViewState::tombstoneEventHandling, uniqueOnly("tombstoneEventHandling")) {
|
||||||
|
renderTombstoneEventHandling(it)
|
||||||
|
}
|
||||||
|
|
||||||
roomDetailViewModel.downloadedFileEvent.observeEvent(this) { downloadFileState ->
|
roomDetailViewModel.downloadedFileEvent.observeEvent(this) { downloadFileState ->
|
||||||
if (downloadFileState.throwable != null) {
|
if (downloadFileState.throwable != null) {
|
||||||
requireActivity().toast(errorFormatter.toHumanReadable(downloadFileState.throwable))
|
requireActivity().toast(errorFormatter.toHumanReadable(downloadFileState.throwable))
|
||||||
|
@ -244,13 +259,8 @@ class RoomDetailFragment :
|
||||||
private fun setupNotificationView() {
|
private fun setupNotificationView() {
|
||||||
notificationAreaView.delegate = object : NotificationAreaView.Delegate {
|
notificationAreaView.delegate = object : NotificationAreaView.Delegate {
|
||||||
|
|
||||||
override fun onUrlClicked(url: String) {
|
override fun onTombstoneEventClicked(tombstoneEvent: Event) {
|
||||||
permalinkHandler.launch(requireActivity(), url, object : NavigateToRoomInterceptor {
|
roomDetailViewModel.process(RoomDetailActions.HandleTombstoneEvent(tombstoneEvent))
|
||||||
override fun navToRoom(roomId: String, eventId: String?): Boolean {
|
|
||||||
requireActivity().finish()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resendUnsentEvents() {
|
override fun resendUnsentEvents() {
|
||||||
|
@ -360,7 +370,7 @@ class RoomDetailFragment :
|
||||||
|
|
||||||
recyclerView.addOnScrollListener(
|
recyclerView.addOnScrollListener(
|
||||||
EndlessRecyclerViewScrollListener(layoutManager, RoomDetailViewModel.PAGINATION_COUNT) { direction ->
|
EndlessRecyclerViewScrollListener(layoutManager, RoomDetailViewModel.PAGINATION_COUNT) { direction ->
|
||||||
roomDetailViewModel.process(RoomDetailActions.LoadMore(direction))
|
roomDetailViewModel.process(RoomDetailActions.LoadMoreTimelineEvents(direction))
|
||||||
})
|
})
|
||||||
recyclerView.setController(timelineEventController)
|
recyclerView.setController(timelineEventController)
|
||||||
timelineEventController.callback = this
|
timelineEventController.callback = this
|
||||||
|
@ -552,7 +562,6 @@ class RoomDetailFragment :
|
||||||
if (summary?.membership == Membership.JOIN) {
|
if (summary?.membership == Membership.JOIN) {
|
||||||
timelineEventController.setTimeline(state.timeline, state.eventId)
|
timelineEventController.setTimeline(state.timeline, state.eventId)
|
||||||
inviteView.visibility = View.GONE
|
inviteView.visibility = View.GONE
|
||||||
|
|
||||||
val uid = session.myUserId
|
val uid = session.myUserId
|
||||||
val meMember = session.getRoom(state.roomId)?.getRoomMember(uid)
|
val meMember = session.getRoom(state.roomId)?.getRoomMember(uid)
|
||||||
avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
|
avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
|
||||||
|
@ -566,14 +575,13 @@ class RoomDetailFragment :
|
||||||
} else if (state.asyncInviter.complete) {
|
} else if (state.asyncInviter.complete) {
|
||||||
vectorBaseActivity.finish()
|
vectorBaseActivity.finish()
|
||||||
}
|
}
|
||||||
|
if (state.tombstoneEvent == null) {
|
||||||
if (state.tombstoneContent == null) {
|
|
||||||
composerLayout.visibility = View.VISIBLE
|
composerLayout.visibility = View.VISIBLE
|
||||||
composerLayout.setRoomEncrypted(state.isEncrypted)
|
composerLayout.setRoomEncrypted(state.isEncrypted)
|
||||||
notificationAreaView.render(NotificationAreaView.State.Hidden)
|
notificationAreaView.render(NotificationAreaView.State.Hidden)
|
||||||
} else {
|
} else {
|
||||||
composerLayout.visibility = View.GONE
|
composerLayout.visibility = View.GONE
|
||||||
notificationAreaView.render(NotificationAreaView.State.Tombstone(state.tombstoneContent))
|
notificationAreaView.render(NotificationAreaView.State.Tombstone(state.tombstoneEvent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,6 +602,26 @@ class RoomDetailFragment :
|
||||||
autocompleteUserPresenter.render(state.asyncUsers)
|
autocompleteUserPresenter.render(state.asyncUsers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun renderTombstoneEventHandling(async: Async<String>) {
|
||||||
|
when (async) {
|
||||||
|
is Loading -> {
|
||||||
|
// TODO Better handling progress
|
||||||
|
vectorBaseActivity.showWaitingView()
|
||||||
|
vectorBaseActivity.waiting_view_status_text.visibility = View.VISIBLE
|
||||||
|
vectorBaseActivity.waiting_view_status_text.text = getString(R.string.join)
|
||||||
|
}
|
||||||
|
is Success -> {
|
||||||
|
navigator.openRoom(vectorBaseActivity, async())
|
||||||
|
vectorBaseActivity.finish()
|
||||||
|
}
|
||||||
|
is Fail -> {
|
||||||
|
vectorBaseActivity.hideWaitingView()
|
||||||
|
vectorBaseActivity.toast(errorFormatter.toHumanReadable(async.error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun renderSendMessageResult(sendMessageResult: SendMessageResult) {
|
private fun renderSendMessageResult(sendMessageResult: SendMessageResult) {
|
||||||
when (sendMessageResult) {
|
when (sendMessageResult) {
|
||||||
is SendMessageResult.MessageSent,
|
is SendMessageResult.MessageSent,
|
||||||
|
@ -627,7 +655,7 @@ class RoomDetailFragment :
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimelineEventController.Callback ************************************************************
|
// TimelineEventController.Callback ************************************************************
|
||||||
|
|
||||||
override fun onUrlClicked(url: String): Boolean {
|
override fun onUrlClicked(url: String): Boolean {
|
||||||
return permalinkHandler.launch(requireActivity(), url, object : NavigateToRoomInterceptor {
|
return permalinkHandler.launch(requireActivity(), url, object : NavigateToRoomInterceptor {
|
||||||
|
@ -760,7 +788,7 @@ class RoomDetailFragment :
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutocompleteUserPresenter.Callback
|
// AutocompleteUserPresenter.Callback
|
||||||
|
|
||||||
override fun onQueryUsers(query: CharSequence?) {
|
override fun onQueryUsers(query: CharSequence?) {
|
||||||
textComposerViewModel.process(TextComposerActions.QueryUsers(query))
|
textComposerViewModel.process(TextComposerActions.QueryUsers(query))
|
||||||
|
@ -862,13 +890,13 @@ class RoomDetailFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//utils
|
//utils
|
||||||
/**
|
/**
|
||||||
* Insert an user displayname in the message editor.
|
* Insert an user displayname in the message editor.
|
||||||
*
|
*
|
||||||
* @param text the text to insert.
|
* @param text the text to insert.
|
||||||
*/
|
*/
|
||||||
//TODO legacy, refactor
|
//TODO legacy, refactor
|
||||||
private fun insertUserDisplayNameInTextEditor(text: String?) {
|
private fun insertUserDisplayNameInTextEditor(text: String?) {
|
||||||
//TODO move logic outside of fragment
|
//TODO move logic outside of fragment
|
||||||
if (null != text) {
|
if (null != text) {
|
||||||
|
@ -919,7 +947,7 @@ class RoomDetailFragment :
|
||||||
snack.show()
|
snack.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
// VectorInviteView.Callback
|
// VectorInviteView.Callback
|
||||||
|
|
||||||
override fun onAcceptInvite() {
|
override fun onAcceptInvite() {
|
||||||
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
|
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
|
||||||
|
|
|
@ -20,7 +20,11 @@ import android.net.Uri
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import arrow.core.success
|
||||||
|
import com.airbnb.mvrx.Async
|
||||||
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
|
import com.airbnb.mvrx.Loading
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
@ -28,6 +32,7 @@ import com.jakewharton.rxrelay2.BehaviorRelay
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.MatrixPatterns
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
@ -49,7 +54,11 @@ import im.vector.riotx.core.utils.LiveEvent
|
||||||
import im.vector.riotx.features.command.CommandParser
|
import im.vector.riotx.features.command.CommandParser
|
||||||
import im.vector.riotx.features.command.ParsedCommand
|
import im.vector.riotx.features.command.ParsedCommand
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
||||||
|
import io.reactivex.Single
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.commonmark.parser.Parser
|
import org.commonmark.parser.Parser
|
||||||
import org.commonmark.renderer.html.HtmlRenderer
|
import org.commonmark.renderer.html.HtmlRenderer
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -107,7 +116,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
is RoomDetailActions.SendMessage -> handleSendMessage(action)
|
is RoomDetailActions.SendMessage -> handleSendMessage(action)
|
||||||
is RoomDetailActions.SendMedia -> handleSendMedia(action)
|
is RoomDetailActions.SendMedia -> handleSendMedia(action)
|
||||||
is RoomDetailActions.EventDisplayed -> handleEventDisplayed(action)
|
is RoomDetailActions.EventDisplayed -> handleEventDisplayed(action)
|
||||||
is RoomDetailActions.LoadMore -> handleLoadMore(action)
|
is RoomDetailActions.LoadMoreTimelineEvents -> handleLoadMore(action)
|
||||||
is RoomDetailActions.SendReaction -> handleSendReaction(action)
|
is RoomDetailActions.SendReaction -> handleSendReaction(action)
|
||||||
is RoomDetailActions.AcceptInvite -> handleAcceptInvite()
|
is RoomDetailActions.AcceptInvite -> handleAcceptInvite()
|
||||||
is RoomDetailActions.RejectInvite -> handleRejectInvite()
|
is RoomDetailActions.RejectInvite -> handleRejectInvite()
|
||||||
|
@ -119,10 +128,42 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
is RoomDetailActions.EnterReplyMode -> handleReplyAction(action)
|
is RoomDetailActions.EnterReplyMode -> handleReplyAction(action)
|
||||||
is RoomDetailActions.DownloadFile -> handleDownloadFile(action)
|
is RoomDetailActions.DownloadFile -> handleDownloadFile(action)
|
||||||
is RoomDetailActions.NavigateToEvent -> handleNavigateToEvent(action)
|
is RoomDetailActions.NavigateToEvent -> handleNavigateToEvent(action)
|
||||||
|
is RoomDetailActions.HandleTombstoneEvent -> handleTombstoneEvent(action)
|
||||||
else -> Timber.e("Unhandled Action: $action")
|
else -> Timber.e("Unhandled Action: $action")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleTombstoneEvent(action: RoomDetailActions.HandleTombstoneEvent) {
|
||||||
|
val tombstoneContent = action.event.getClearContent().toModel<RoomTombstoneContent>()
|
||||||
|
?: return
|
||||||
|
|
||||||
|
val roomId = tombstoneContent.replacementRoom
|
||||||
|
// TODO replace with rx flux
|
||||||
|
if (session.getRoom(roomId)?.roomSummary()?.membership == Membership.JOIN) {
|
||||||
|
setState { copy(tombstoneEventHandling = Success(roomId)) }
|
||||||
|
} else {
|
||||||
|
val viaServer = MatrixPatterns.extractServerNameFromId(action.event.senderId).let {
|
||||||
|
if (it.isNullOrBlank()) {
|
||||||
|
emptyList()
|
||||||
|
} else {
|
||||||
|
listOf(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState { copy(tombstoneEventHandling = Loading()) }
|
||||||
|
session.joinRoom(roomId, viaServer, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
setState { copy(tombstoneEventHandling = Success(roomId)) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
setState { copy(tombstoneEventHandling = Fail(failure)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private fun enterEditMode(event: TimelineEvent) {
|
private fun enterEditMode(event: TimelineEvent) {
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
|
@ -143,7 +184,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
val nonBlockingPopAlert: LiveData<LiveEvent<Pair<Int, List<Any>>>>
|
val nonBlockingPopAlert: LiveData<LiveEvent<Pair<Int, List<Any>>>>
|
||||||
get() = _nonBlockingPopAlert
|
get() = _nonBlockingPopAlert
|
||||||
|
|
||||||
|
|
||||||
private val _sendMessageResultLiveData = MutableLiveData<LiveEvent<SendMessageResult>>()
|
private val _sendMessageResultLiveData = MutableLiveData<LiveEvent<SendMessageResult>>()
|
||||||
val sendMessageResultLiveData: LiveData<LiveEvent<SendMessageResult>>
|
val sendMessageResultLiveData: LiveData<LiveEvent<SendMessageResult>>
|
||||||
get() = _sendMessageResultLiveData
|
get() = _sendMessageResultLiveData
|
||||||
|
@ -232,7 +272,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
|
|
||||||
//is original event a reply?
|
//is original event a reply?
|
||||||
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
|
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
|
||||||
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
|
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
|
||||||
if (inReplyTo != null) {
|
if (inReplyTo != null) {
|
||||||
//TODO check if same content?
|
//TODO check if same content?
|
||||||
room.getTimeLineEvent(inReplyTo)?.let {
|
room.getTimeLineEvent(inReplyTo)?.let {
|
||||||
|
@ -241,12 +281,12 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
} else {
|
} else {
|
||||||
val messageContent: MessageContent? =
|
val messageContent: MessageContent? =
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||||
val existingBody = messageContent?.body ?: ""
|
val existingBody = messageContent?.body ?: ""
|
||||||
if (existingBody != action.text) {
|
if (existingBody != action.text) {
|
||||||
room.editTextMessage(state.sendMode.timelineEvent.root.eventId
|
room.editTextMessage(state.sendMode.timelineEvent.root.eventId
|
||||||
?: "", messageContent?.type
|
?: "", messageContent?.type
|
||||||
?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
|
?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
|
||||||
} else {
|
} else {
|
||||||
Timber.w("Same message content, do not send edition")
|
Timber.w("Same message content, do not send edition")
|
||||||
}
|
}
|
||||||
|
@ -261,7 +301,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
is SendMode.QUOTE -> {
|
is SendMode.QUOTE -> {
|
||||||
val messageContent: MessageContent? =
|
val messageContent: MessageContent? =
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||||
val textMsg = messageContent?.body
|
val textMsg = messageContent?.body
|
||||||
|
|
||||||
val finalText = legacyRiotQuoteText(textMsg, action.text)
|
val finalText = legacyRiotQuoteText(textMsg, action.text)
|
||||||
|
@ -401,7 +441,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLoadMore(action: RoomDetailActions.LoadMore) {
|
private fun handleLoadMore(action: RoomDetailActions.LoadMoreTimelineEvents) {
|
||||||
timeline.paginate(action.direction, PAGINATION_COUNT)
|
timeline.paginate(action.direction, PAGINATION_COUNT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,7 +450,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAcceptInvite() {
|
private fun handleAcceptInvite() {
|
||||||
room.join(object : MatrixCallback<Unit> {})
|
room.join(callback = object : MatrixCallback<Unit> {})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEditAction(action: RoomDetailActions.EnterEditMode) {
|
private fun handleEditAction(action: RoomDetailActions.EnterEditMode) {
|
||||||
|
@ -558,12 +598,8 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
setState { copy(asyncInviter = Success(it)) }
|
setState { copy(asyncInviter = Success(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (summary.isVersioned) {
|
room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE)?.also {
|
||||||
room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE)
|
setState { copy(tombstoneEvent = it) }
|
||||||
?.getClearContent()
|
|
||||||
?.toModel<RoomTombstoneContent>()?.also {
|
|
||||||
setState { copy(tombstoneContent = it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.riotx.features.home.room.detail
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
|
@ -48,7 +49,8 @@ data class RoomDetailViewState(
|
||||||
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
|
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
|
||||||
val sendMode: SendMode = SendMode.REGULAR,
|
val sendMode: SendMode = SendMode.REGULAR,
|
||||||
val isEncrypted: Boolean = false,
|
val isEncrypted: Boolean = false,
|
||||||
val tombstoneContent: RoomTombstoneContent? = null
|
val tombstoneEvent: Event? = null,
|
||||||
|
val tombstoneEventHandling: Async<String> = Uninitialized
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
||||||
|
|
|
@ -134,7 +134,7 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
session.getRoom(roomId)?.join(object : MatrixCallback<Unit> {
|
session.getRoom(roomId)?.join(emptyList(), object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
// Instead, we wait for the room to be joined
|
// Instead, we wait for the room to be joined
|
||||||
|
|
|
@ -75,7 +75,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
private fun handleJoinRoom(roomId: String) {
|
private fun handleJoinRoom(roomId: String) {
|
||||||
activeSessionHolder.getSafeActiveSession()?.let { session ->
|
activeSessionHolder.getSafeActiveSession()?.let { session ->
|
||||||
session.getRoom(roomId)
|
session.getRoom(roomId)
|
||||||
?.join(object : MatrixCallback<Unit> {})
|
?.join(emptyList(), object : MatrixCallback<Unit> {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
session.joinRoom(publicRoom.roomId, object : MatrixCallback<Unit> {
|
session.joinRoom(publicRoom.roomId, emptyList(), object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
// Instead, we wait for the room to be joined
|
// Instead, we wait for the room to be joined
|
||||||
|
|
|
@ -90,7 +90,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
session.joinRoom(state.roomId, object : MatrixCallback<Unit> {
|
session.joinRoom(state.roomId, emptyList(), object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
// Instead, we wait for the room to be joined
|
// Instead, we wait for the room to be joined
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/roomDetailContainer"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/roomDetailContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<include layout="@layout/merge_overlay_waiting_view" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
Loading…
Reference in a new issue