Merge pull request #923 from vector-im/feature/xcrossing_fix
Decoration in room profile and improve Rx flow
This commit is contained in:
commit
2616a889ef
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package im.vector.matrix.rx
|
package im.vector.matrix.rx
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
|
||||||
|
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.events.model.Event
|
||||||
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.members.RoomMemberQueryParams
|
import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams
|
||||||
|
@ -30,12 +32,58 @@ import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.api.util.toOptional
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
import io.reactivex.functions.BiFunction
|
||||||
|
|
||||||
class RxRoom(private val room: Room) {
|
class RxRoom(private val room: Room, private val session: Session) {
|
||||||
|
|
||||||
fun liveRoomSummary(): Observable<Optional<RoomSummary>> {
|
fun liveRoomSummary(): Observable<Optional<RoomSummary>> {
|
||||||
return room.getRoomSummaryLive().asObservable()
|
val summaryObservable = room.getRoomSummaryLive()
|
||||||
|
.asObservable()
|
||||||
.startWith(room.roomSummary().toOptional())
|
.startWith(room.roomSummary().toOptional())
|
||||||
|
|
||||||
|
val memberIdsChangeObservable = summaryObservable
|
||||||
|
.map {
|
||||||
|
it.getOrNull()?.let { roomSummary ->
|
||||||
|
if (roomSummary.isEncrypted) {
|
||||||
|
// Return the list of other users
|
||||||
|
roomSummary.otherMemberIds
|
||||||
|
} else {
|
||||||
|
// Return an empty list, the room is not encrypted
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}.orEmpty()
|
||||||
|
}.distinctUntilChanged()
|
||||||
|
|
||||||
|
// Observe the device info of the users in the room
|
||||||
|
val cryptoDeviceInfoObservable = memberIdsChangeObservable
|
||||||
|
.switchMap { otherUserIds ->
|
||||||
|
session.getLiveCryptoDeviceInfo(otherUserIds)
|
||||||
|
.asObservable()
|
||||||
|
.map {
|
||||||
|
// If any key change, emit the userIds list
|
||||||
|
otherUserIds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val roomEncryptionTrustLevelObservable = cryptoDeviceInfoObservable
|
||||||
|
.map { otherUserIds ->
|
||||||
|
if (otherUserIds.isEmpty()) {
|
||||||
|
Optional<RoomEncryptionTrustLevel>(null)
|
||||||
|
} else {
|
||||||
|
session.getCrossSigningService().getTrustLevelForUsers(otherUserIds).toOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Observable
|
||||||
|
.combineLatest<Optional<RoomSummary>, Optional<RoomEncryptionTrustLevel>, Optional<RoomSummary>>(
|
||||||
|
summaryObservable,
|
||||||
|
roomEncryptionTrustLevelObservable,
|
||||||
|
BiFunction { summary, level ->
|
||||||
|
summary.getOrNull()?.copy(
|
||||||
|
roomEncryptionTrustLevel = level.getOrNull()
|
||||||
|
).toOptional()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable<List<RoomMemberSummary>> {
|
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable<List<RoomMemberSummary>> {
|
||||||
|
@ -88,6 +136,6 @@ class RxRoom(private val room: Room) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Room.rx(): RxRoom {
|
fun Room.rx(session: Session): RxRoom {
|
||||||
return RxRoom(this)
|
return RxRoom(this, session)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,32 @@ import im.vector.matrix.android.api.util.toOptional
|
||||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
import io.reactivex.functions.BiFunction
|
||||||
|
|
||||||
class RxSession(private val session: Session) {
|
class RxSession(private val session: Session) {
|
||||||
|
|
||||||
fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Observable<List<RoomSummary>> {
|
fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Observable<List<RoomSummary>> {
|
||||||
return session.getRoomSummariesLive(queryParams).asObservable()
|
val summariesObservable = session.getRoomSummariesLive(queryParams).asObservable()
|
||||||
.startWith(session.getRoomSummaries(queryParams))
|
.startWith(session.getRoomSummaries(queryParams))
|
||||||
|
|
||||||
|
val cryptoDeviceInfoObservable = session.getLiveCryptoDeviceInfo().asObservable()
|
||||||
|
|
||||||
|
return Observable
|
||||||
|
.combineLatest<List<RoomSummary>, List<CryptoDeviceInfo>, List<RoomSummary>>(
|
||||||
|
summariesObservable,
|
||||||
|
cryptoDeviceInfoObservable,
|
||||||
|
BiFunction { summaries, _ ->
|
||||||
|
summaries.map {
|
||||||
|
if (it.isEncrypted) {
|
||||||
|
it.copy(
|
||||||
|
roomEncryptionTrustLevel = session.getCrossSigningService().getTrustLevelForUsers(it.otherMemberIds)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Observable<List<GroupSummary>> {
|
fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Observable<List<GroupSummary>> {
|
||||||
|
|
|
@ -120,8 +120,12 @@ interface CryptoService {
|
||||||
|
|
||||||
fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo>
|
fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo>
|
||||||
|
|
||||||
|
fun getLiveCryptoDeviceInfo(): LiveData<List<CryptoDeviceInfo>>
|
||||||
|
|
||||||
fun getLiveCryptoDeviceInfo(userId: String): LiveData<List<CryptoDeviceInfo>>
|
fun getLiveCryptoDeviceInfo(userId: String): LiveData<List<CryptoDeviceInfo>>
|
||||||
|
|
||||||
|
fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>>
|
||||||
|
|
||||||
fun addNewSessionListener(newSessionListener: NewSessionListener)
|
fun addNewSessionListener(newSessionListener: NewSessionListener)
|
||||||
|
|
||||||
fun removeSessionListener(listener: NewSessionListener)
|
fun removeSessionListener(listener: NewSessionListener)
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.crypto.crosssigning
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustResult
|
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustResult
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.UserTrustResult
|
import im.vector.matrix.android.internal.crypto.crosssigning.UserTrustResult
|
||||||
|
@ -62,4 +63,6 @@ interface CrossSigningService {
|
||||||
fun checkDeviceTrust(otherUserId: String,
|
fun checkDeviceTrust(otherUserId: String,
|
||||||
otherDeviceId: String,
|
otherDeviceId: String,
|
||||||
locallyTrusted: Boolean?): DeviceTrustResult
|
locallyTrusted: Boolean?): DeviceTrustResult
|
||||||
|
|
||||||
|
fun getTrustLevelForUsers(userIds: List<String>): RoomEncryptionTrustLevel
|
||||||
}
|
}
|
||||||
|
|
|
@ -403,10 +403,18 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
return cryptoStore.getUserDevices(userId)?.map { it.value } ?: emptyList()
|
return cryptoStore.getUserDevices(userId)?.map { it.value } ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getLiveCryptoDeviceInfo(): LiveData<List<CryptoDeviceInfo>> {
|
||||||
|
return cryptoStore.getLiveDeviceList()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getLiveCryptoDeviceInfo(userId: String): LiveData<List<CryptoDeviceInfo>> {
|
override fun getLiveCryptoDeviceInfo(userId: String): LiveData<List<CryptoDeviceInfo>> {
|
||||||
return cryptoStore.getLiveDeviceList(userId)
|
return cryptoStore.getLiveDeviceList(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>> {
|
||||||
|
return cryptoStore.getLiveDeviceList(userIds)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the devices as known
|
* Set the devices as known
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,6 +19,8 @@ package im.vector.matrix.android.internal.crypto.crosssigning
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
|
||||||
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
@ -655,4 +657,35 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getTrustLevelForUsers(userIds: List<String>): RoomEncryptionTrustLevel {
|
||||||
|
val atLeastOneTrusted = userIds
|
||||||
|
.filter { it != userId }
|
||||||
|
.map { getUserCrossSigningKeys(it) }
|
||||||
|
.any { it?.isTrusted() == true }
|
||||||
|
|
||||||
|
return if (!atLeastOneTrusted) {
|
||||||
|
RoomEncryptionTrustLevel.Default
|
||||||
|
} else {
|
||||||
|
// I have verified at least one other user
|
||||||
|
val allDevices = userIds.mapNotNull {
|
||||||
|
cryptoStore.getUserDeviceList(it)
|
||||||
|
}.flatten()
|
||||||
|
if (getMyCrossSigningKeys() != null) {
|
||||||
|
val hasWarning = allDevices.any { !it.trustLevel?.crossSigningVerified.orFalse() }
|
||||||
|
if (hasWarning) {
|
||||||
|
RoomEncryptionTrustLevel.Warning
|
||||||
|
} else {
|
||||||
|
RoomEncryptionTrustLevel.Trusted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val hasWarningLegacy = allDevices.any { !it.isVerified }
|
||||||
|
if (hasWarningLegacy) {
|
||||||
|
RoomEncryptionTrustLevel.Warning
|
||||||
|
} else {
|
||||||
|
RoomEncryptionTrustLevel.Trusted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,6 +200,11 @@ internal interface IMXCryptoStore {
|
||||||
fun getUserDeviceList(userId: String): List<CryptoDeviceInfo>?
|
fun getUserDeviceList(userId: String): List<CryptoDeviceInfo>?
|
||||||
|
|
||||||
fun getLiveDeviceList(userId: String): LiveData<List<CryptoDeviceInfo>>
|
fun getLiveDeviceList(userId: String): LiveData<List<CryptoDeviceInfo>>
|
||||||
|
|
||||||
|
fun getLiveDeviceList(userIds: List<String>): LiveData<List<CryptoDeviceInfo>>
|
||||||
|
|
||||||
|
// TODO temp
|
||||||
|
fun getLiveDeviceList(): LiveData<List<CryptoDeviceInfo>>
|
||||||
/**
|
/**
|
||||||
* Store the crypto algorithm for a room.
|
* Store the crypto algorithm for a room.
|
||||||
*
|
*
|
||||||
|
|
|
@ -398,6 +398,36 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getLiveDeviceList(userIds: List<String>): LiveData<List<CryptoDeviceInfo>> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{ realm: Realm ->
|
||||||
|
realm
|
||||||
|
.where<UserEntity>()
|
||||||
|
.`in`(UserEntityFields.USER_ID, userIds.toTypedArray())
|
||||||
|
},
|
||||||
|
{ entity ->
|
||||||
|
entity.devices.map { CryptoMapper.mapToModel(it) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return Transformations.map(liveData) {
|
||||||
|
it.firstOrNull() ?: emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLiveDeviceList(): LiveData<List<CryptoDeviceInfo>> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{ realm: Realm ->
|
||||||
|
realm.where<UserEntity>()
|
||||||
|
},
|
||||||
|
{ entity ->
|
||||||
|
entity.devices.map { CryptoMapper.mapToModel(it) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return Transformations.map(liveData) {
|
||||||
|
it.firstOrNull() ?: emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun storeRoomAlgorithm(roomId: String, algorithm: String) {
|
override fun storeRoomAlgorithm(roomId: String, algorithm: String) {
|
||||||
doRealmTransaction(realmConfiguration) {
|
doRealmTransaction(realmConfiguration) {
|
||||||
CryptoRoomEntity.getOrCreate(it, roomId).algorithm = algorithm
|
CryptoRoomEntity.getOrCreate(it, roomId).algorithm = algorithm
|
||||||
|
|
|
@ -27,7 +27,8 @@ abstract class AppBarStateChangeListener : OnOffsetChangedListener {
|
||||||
EXPANDED, COLLAPSED, IDLE
|
EXPANDED, COLLAPSED, IDLE
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentState = State.IDLE
|
var currentState = State.IDLE
|
||||||
|
private set
|
||||||
|
|
||||||
override fun onOffsetChanged(appBarLayout: AppBarLayout, i: Int) {
|
override fun onOffsetChanged(appBarLayout: AppBarLayout, i: Int) {
|
||||||
currentState = if (i == 0) {
|
currentState = if (i == 0) {
|
||||||
|
|
|
@ -152,7 +152,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
observeDrafts()
|
observeDrafts()
|
||||||
observeUnreadState()
|
observeUnreadState()
|
||||||
room.getRoomSummaryLive()
|
room.getRoomSummaryLive()
|
||||||
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
room.rx(session).loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
||||||
// Inform the SDK that the room is displayed
|
// Inform the SDK that the room is displayed
|
||||||
session.onRoomDisplayed(initialState.roomId)
|
session.onRoomDisplayed(initialState.roomId)
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeDrafts() {
|
private fun observeDrafts() {
|
||||||
room.rx().liveDrafts()
|
room.rx(session).liveDrafts()
|
||||||
.subscribe {
|
.subscribe {
|
||||||
Timber.d("Draft update --> SetState")
|
Timber.d("Draft update --> SetState")
|
||||||
setState {
|
setState {
|
||||||
|
@ -874,7 +874,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room.rx().liveRoomSummary()
|
room.rx(session).liveRoomSummary()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
val typingRoomMembers =
|
val typingRoomMembers =
|
||||||
|
@ -892,7 +892,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
Observable
|
Observable
|
||||||
.combineLatest<List<TimelineEvent>, RoomSummary, UnreadState>(
|
.combineLatest<List<TimelineEvent>, RoomSummary, UnreadState>(
|
||||||
timelineEvents.observeOn(Schedulers.computation()),
|
timelineEvents.observeOn(Schedulers.computation()),
|
||||||
room.rx().liveRoomSummary().unwrap(),
|
room.rx(session).liveRoomSummary().unwrap(),
|
||||||
BiFunction { timelineEvents, roomSummary ->
|
BiFunction { timelineEvents, roomSummary ->
|
||||||
computeUnreadState(timelineEvents, roomSummary)
|
computeUnreadState(timelineEvents, roomSummary)
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||||
|
|
||||||
private fun observeEvent() {
|
private fun observeEvent() {
|
||||||
if (room == null) return
|
if (room == null) return
|
||||||
room.rx()
|
room.rx(session)
|
||||||
.liveTimelineEvent(eventId)
|
.liveTimelineEvent(eventId)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute {
|
.execute {
|
||||||
|
@ -144,7 +144,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||||
|
|
||||||
private fun observeReactions() {
|
private fun observeReactions() {
|
||||||
if (room == null) return
|
if (room == null) return
|
||||||
room.rx()
|
room.rx(session)
|
||||||
.liveAnnotationSummary(eventId)
|
.liveAnnotationSummary(eventId)
|
||||||
.map { annotations ->
|
.map { annotations ->
|
||||||
EmojiDataSource.quickEmojis.map { emoji ->
|
EmojiDataSource.quickEmojis.map { emoji ->
|
||||||
|
|
|
@ -86,7 +86,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeEventAnnotationSummaries() {
|
private fun observeEventAnnotationSummaries() {
|
||||||
RxRoom(room)
|
RxRoom(room, session)
|
||||||
.liveAnnotationSummary(eventId)
|
.liveAnnotationSummary(eventId)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.flatMapSingle { summaries ->
|
.flatMapSingle { summaries ->
|
||||||
|
|
|
@ -28,7 +28,7 @@ import im.vector.riotx.core.platform.EmptyViewEvents
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
|
||||||
class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState,
|
class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState,
|
||||||
session: Session
|
private val session: Session
|
||||||
) : VectorViewModel<RoomListQuickActionsState, EmptyAction, EmptyViewEvents>(initialState) {
|
) : VectorViewModel<RoomListQuickActionsState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
@AssistedInject.Factory
|
@AssistedInject.Factory
|
||||||
|
@ -54,7 +54,7 @@ class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initia
|
||||||
|
|
||||||
private fun observeNotificationState() {
|
private fun observeNotificationState() {
|
||||||
room
|
room
|
||||||
.rx()
|
.rx(session)
|
||||||
.liveNotificationState()
|
.liveNotificationState()
|
||||||
.execute {
|
.execute {
|
||||||
copy(roomNotificationState = it)
|
copy(roomNotificationState = it)
|
||||||
|
@ -63,7 +63,7 @@ class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initia
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room
|
room
|
||||||
.rx()
|
.rx(session)
|
||||||
.liveRoomSummary()
|
.liveRoomSummary()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute {
|
.execute {
|
||||||
|
|
|
@ -153,7 +153,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
|
||||||
val queryParams = roomMemberQueryParams {
|
val queryParams = roomMemberQueryParams {
|
||||||
this.userId = QueryStringValue.Equals(initialState.userId, QueryStringValue.Case.SENSITIVE)
|
this.userId = QueryStringValue.Equals(initialState.userId, QueryStringValue.Case.SENSITIVE)
|
||||||
}
|
}
|
||||||
room.rx().liveRoomMembers(queryParams)
|
room.rx(session).liveRoomMembers(queryParams)
|
||||||
.map { it.firstOrNull()?.toMatrixItem().toOptional() }
|
.map { it.firstOrNull()?.toMatrixItem().toOptional() }
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute {
|
.execute {
|
||||||
|
@ -176,8 +176,8 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummaryAndPowerLevels(room: Room) {
|
private fun observeRoomSummaryAndPowerLevels(room: Room) {
|
||||||
val roomSummaryLive = room.rx().liveRoomSummary().unwrap()
|
val roomSummaryLive = room.rx(session).liveRoomSummary().unwrap()
|
||||||
val powerLevelsContentLive = room.rx().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
val powerLevelsContentLive = room.rx(session).liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
||||||
.mapOptional { it.content.toModel<PowerLevelsContent>() }
|
.mapOptional { it.content.toModel<PowerLevelsContent>() }
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
|
@ -34,6 +35,7 @@ import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.extensions.exhaustive
|
import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.extensions.setTextOrHide
|
import im.vector.riotx.core.extensions.setTextOrHide
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.riotx.features.crypto.util.toImageRes
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import im.vector.riotx.features.home.room.list.actions.RoomListActionsArgs
|
import im.vector.riotx.features.home.room.list.actions.RoomListActionsArgs
|
||||||
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||||
|
@ -75,8 +77,12 @@ class RoomProfileFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
setupToolbar(matrixProfileToolbar)
|
setupToolbar(matrixProfileToolbar)
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView,
|
appBarStateChangeListener = MatrixItemAppBarStateChangeListener(
|
||||||
matrixProfileToolbarTitleView))
|
headerView,
|
||||||
|
listOf(matrixProfileToolbarAvatarImageView,
|
||||||
|
matrixProfileToolbarTitleView,
|
||||||
|
matrixProfileDecorationToolbarAvatarImageView)
|
||||||
|
)
|
||||||
matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
|
matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
|
||||||
roomProfileViewModel.observeViewEvents {
|
roomProfileViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
|
@ -139,6 +145,9 @@ class RoomProfileFragment @Inject constructor(
|
||||||
val matrixItem = it.toMatrixItem()
|
val matrixItem = it.toMatrixItem()
|
||||||
avatarRenderer.render(matrixItem, roomProfileAvatarView)
|
avatarRenderer.render(matrixItem, roomProfileAvatarView)
|
||||||
avatarRenderer.render(matrixItem, matrixProfileToolbarAvatarImageView)
|
avatarRenderer.render(matrixItem, matrixProfileToolbarAvatarImageView)
|
||||||
|
roomProfileDecorationImageView.isVisible = it.roomEncryptionTrustLevel != null
|
||||||
|
roomProfileDecorationImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes())
|
||||||
|
matrixProfileDecorationToolbarAvatarImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roomProfileController.setData(state)
|
roomProfileController.setData(state)
|
||||||
|
|
|
@ -56,7 +56,7 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room.rx().liveRoomSummary()
|
room.rx(session).liveRoomSummary()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute {
|
.execute {
|
||||||
copy(roomSummary = it)
|
copy(roomSummary = it)
|
||||||
|
|
|
@ -72,8 +72,8 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
|
||||||
}
|
}
|
||||||
Observable
|
Observable
|
||||||
.combineLatest<List<RoomMemberSummary>, PowerLevelsContent, RoomMemberSummaries>(
|
.combineLatest<List<RoomMemberSummary>, PowerLevelsContent, RoomMemberSummaries>(
|
||||||
room.rx().liveRoomMembers(roomMemberQueryParams),
|
room.rx(session).liveRoomMembers(roomMemberQueryParams),
|
||||||
room.rx()
|
room.rx(session)
|
||||||
.liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
.liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
||||||
.mapOptional { it.content.toModel<PowerLevelsContent>() }
|
.mapOptional { it.content.toModel<PowerLevelsContent>() }
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -87,7 +87,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room.rx().liveRoomSummary()
|
room.rx(session).liveRoomSummary()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
copy(roomSummary = async)
|
copy(roomSummary = async)
|
||||||
|
|
|
@ -53,7 +53,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room.rx().liveRoomSummary()
|
room.rx(session).liveRoomSummary()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
copy(roomSummary = async)
|
copy(roomSummary = async)
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:contentScrim="?riotx_background"
|
app:contentScrim="?riotx_background"
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
|
||||||
app:scrimVisibleHeightTrigger="80dp"
|
|
||||||
app:scrimAnimationDuration="250"
|
app:scrimAnimationDuration="250"
|
||||||
|
app:scrimVisibleHeightTrigger="80dp"
|
||||||
app:titleEnabled="false"
|
app:titleEnabled="false"
|
||||||
app:toolbarId="@+id/matrixProfileToolbar">
|
app:toolbarId="@+id/matrixProfileToolbar">
|
||||||
|
|
||||||
|
@ -59,6 +59,16 @@
|
||||||
tools:alpha="1"
|
tools:alpha="1"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/matrixProfileDecorationToolbarAvatarImageView"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
app:layout_constraintCircle="@+id/matrixProfileToolbarAvatarImageView"
|
||||||
|
app:layout_constraintCircleAngle="135"
|
||||||
|
app:layout_constraintCircleRadius="20dp"
|
||||||
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:src="@drawable/ic_shield_trusted" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/matrixProfileToolbarTitleView"
|
android:id="@+id/matrixProfileToolbarTitleView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -1,49 +1,68 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?riotx_background"
|
android:background="?riotx_background"
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/roomProfileAvatarView"
|
android:id="@+id/roomProfileAvatarView"
|
||||||
android:layout_width="128dp"
|
android:layout_width="128dp"
|
||||||
android:layout_height="128dp"
|
android:layout_height="128dp"
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/roomProfileNameView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/roomProfileDecorationImageView"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
app:layout_constraintCircle="@+id/roomProfileAvatarView"
|
||||||
|
app:layout_constraintCircleAngle="135"
|
||||||
|
app:layout_constraintCircleRadius="64dp"
|
||||||
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:src="@drawable/ic_shield_trusted" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/roomProfileNameView"
|
android:id="@+id/roomProfileNameView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
android:gravity="center"
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textAppearance="@style/Vector.Toolbar.Title"
|
android:textAppearance="@style/Vector.Toolbar.Title"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/roomProfileAliasView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomProfileAvatarView"
|
||||||
tools:text="@sample/matrix.json/data/roomName" />
|
tools:text="@sample/matrix.json/data/roomName" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/roomProfileAliasView"
|
android:id="@+id/roomProfileAliasView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="@style/Vector.Toolbar.Title"
|
android:textAppearance="@style/Vector.Toolbar.Title"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/roomProfileTopicView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomProfileNameView"
|
||||||
tools:text="@sample/matrix.json/data/roomAlias" />
|
tools:text="@sample/matrix.json/data/roomAlias" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/roomProfileTopicView"
|
android:id="@+id/roomProfileTopicView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginStart="40dp"
|
android:layout_marginStart="40dp"
|
||||||
android:layout_marginEnd="40dp"
|
android:layout_marginEnd="40dp"
|
||||||
android:autoLink="web"
|
android:autoLink="web"
|
||||||
|
@ -51,6 +70,10 @@
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="normal"
|
android:textStyle="normal"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomProfileAliasView"
|
||||||
tools:text="@sample/matrix.json/data/roomTopic" />
|
tools:text="@sample/matrix.json/data/roomTopic" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
Loading…
Reference in New Issue