Tombstone : handle joining viaserver params

This commit is contained in:
ganfra 2019-07-26 19:17:12 +02:00
parent 9a1e16a170
commit ac38a6461c
28 changed files with 293 additions and 82 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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>
/** /**

View file

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

View file

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

View file

@ -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())
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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> {})
} }
} }

View file

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

View file

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

View file

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