Introduce a very simple query langage and refact autocomplete

This commit is contained in:
ganfra 2020-01-08 22:17:32 +01:00
parent c60b4ddb5a
commit 383605274c
31 changed files with 417 additions and 433 deletions

View File

@ -17,7 +17,11 @@
package im.vector.matrix.rx package im.vector.matrix.rx
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.model.* import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
import im.vector.matrix.android.api.session.room.send.UserDraft import im.vector.matrix.android.api.session.room.send.UserDraft
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
@ -33,9 +37,9 @@ class RxRoom(private val room: Room) {
.startWith(room.roomSummary().toOptional()) .startWith(room.roomSummary().toOptional())
} }
fun liveRoomMembers(memberships: List<Membership>): Observable<List<RoomMember>> { fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable<List<RoomMember>> {
return room.getRoomMembersLive(memberships).asObservable() return room.getRoomMembersLive(queryParams).asObservable()
.startWith(room.getRoomMembers(memberships)) .startWith(room.getRoomMembers(queryParams))
} }
fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> { fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> {

View File

@ -18,6 +18,7 @@ package im.vector.matrix.rx
import androidx.paging.PagedList import androidx.paging.PagedList
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.group.GroupSummaryQueryParams
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.pushers.Pusher
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
@ -36,9 +37,9 @@ class RxSession(private val session: Session) {
.startWith(session.getRoomSummaries(queryParams)) .startWith(session.getRoomSummaries(queryParams))
} }
fun liveGroupSummaries(): Observable<List<GroupSummary>> { fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Observable<List<GroupSummary>> {
return session.getGroupSummariesLive().asObservable() return session.getGroupSummariesLive(queryParams).asObservable()
.startWith(session.getGroupSummaries()) .startWith(session.getGroupSummaries(queryParams))
} }
fun liveBreadcrumbs(): Observable<List<RoomSummary>> { fun liveBreadcrumbs(): Observable<List<RoomSummary>> {

View File

@ -0,0 +1,35 @@
/*
* Copyright 2020 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.query
/**
* Basic query language. All these cases are mutually exclusive.
*/
sealed class QueryStringValue {
object NoCondition : QueryStringValue()
object IsNull : QueryStringValue()
object IsNotNull : QueryStringValue()
object IsEmpty : QueryStringValue()
object IsNotEmpty : QueryStringValue()
data class Equals(val string: String, val case: Case) : QueryStringValue()
data class Contains(val string: String, val case: Case) : QueryStringValue()
enum class Case {
SENSITIVE,
INSENSITIVE
}
}

View File

@ -42,11 +42,11 @@ interface GroupService {
* Get a list of group summaries.This list is a snapshot of the data. * Get a list of group summaries.This list is a snapshot of the data.
* @return the list of [GroupSummary] * @return the list of [GroupSummary]
*/ */
fun getGroupSummaries(): List<GroupSummary> fun getGroupSummaries(groupSummaryQueryParams: GroupSummaryQueryParams): List<GroupSummary>
/** /**
* Get a live list of group summaries. This list is refreshed as soon as the data changes. * Get a live list of group summaries. This list is refreshed as soon as the data changes.
* @return the [LiveData] of [GroupSummary] * @return the [LiveData] of [GroupSummary]
*/ */
fun getGroupSummariesLive(): LiveData<List<GroupSummary>> fun getGroupSummariesLive(groupSummaryQueryParams: GroupSummaryQueryParams): LiveData<List<GroupSummary>>
} }

View File

@ -0,0 +1,44 @@
/*
* Copyright 2020 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.group
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.room.model.Membership
fun groupSummaryQueryParams(init: (GroupSummaryQueryParams.Builder.() -> Unit) = {}): GroupSummaryQueryParams {
return GroupSummaryQueryParams.Builder().apply(init).build()
}
/**
* This class can be used to filter group summaries
*/
data class GroupSummaryQueryParams(
val displayName: QueryStringValue,
val memberships: List<Membership>
) {
class Builder {
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
var memberships: List<Membership> = Membership.all()
fun build() = GroupSummaryQueryParams(
displayName = displayName,
memberships = memberships
)
}
}

View File

@ -16,23 +16,33 @@
package im.vector.matrix.android.api.session.room package im.vector.matrix.android.api.session.room
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
fun roomSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) = {}): RoomSummaryQueryParams {
return RoomSummaryQueryParams.Builder().apply(init).build()
}
/** /**
* This class can be used to filter room summaries to use with: * This class can be used to filter room summaries to use with:
* [im.vector.matrix.android.api.session.room.Room] and [im.vector.matrix.android.api.session.room.RoomService] * [im.vector.matrix.android.api.session.room.Room] and [im.vector.matrix.android.api.session.room.RoomService]
*/ */
data class RoomSummaryQueryParams( data class RoomSummaryQueryParams(
/** val displayName: QueryStringValue,
* Set to true if you want only non null display name. True by default val canonicalAlias: QueryStringValue,
*/ val memberships: List<Membership>
val filterDisplayName: Boolean = true, ) {
/**
* Set to true if you want only non null canonical alias. False by default. class Builder {
*/
val filterCanonicalAlias: Boolean = false, var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
/** var canonicalAlias: QueryStringValue = QueryStringValue.NoCondition
* Set the list of memberships you want to filter on. By default, all memberships. var memberships: List<Membership> = Membership.all()
*/
val memberships: List<Membership> = Membership.all() fun build() = RoomSummaryQueryParams(
) displayName = displayName,
canonicalAlias = canonicalAlias,
memberships = memberships
)
}
}

View File

@ -18,7 +18,6 @@ package im.vector.matrix.android.api.session.room.members
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.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
@ -41,20 +40,19 @@ interface MembershipService {
*/ */
fun getRoomMember(userId: String): RoomMember? fun getRoomMember(userId: String): RoomMember?
/** /**
* Return all the roomMembers of the room filtered by memberships * Return all the roomMembers of the room with params
* @param memberships list of accepted memberships * @param queryParams the params to query for
* @return a roomMember list. * @return a roomMember list.
*/ */
fun getRoomMembers(memberships: List<Membership>): List<RoomMember> fun getRoomMembers(queryParams: RoomMemberQueryParams): List<RoomMember>
/** /**
* Return all the roomMembers of the room filtered by memberships * Return all the roomMembers of the room filtered by memberships
* @param memberships list of accepted memberships * @param queryParams the params to query for
* @return a [LiveData] of roomMember list. * @return a [LiveData] of roomMember list.
*/ */
fun getRoomMembersLive(memberships: List<Membership>): LiveData<List<RoomMember>> fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData<List<RoomMember>>
fun getNumberOfJoinedMembers(): Int fun getNumberOfJoinedMembers(): Int

View File

@ -0,0 +1,44 @@
/*
* Copyright 2020 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.members
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.room.model.Membership
fun roomMemberQueryParams(init: (RoomMemberQueryParams.Builder.() -> Unit) = {}): RoomMemberQueryParams {
return RoomMemberQueryParams.Builder().apply(init).build()
}
/**
* This class can be used to filter room members
*/
data class RoomMemberQueryParams(
val displayName: QueryStringValue,
val memberships: List<Membership>
) {
class Builder {
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
var memberships: List<Membership> = Membership.all()
fun build() = RoomMemberQueryParams(
displayName = displayName,
memberships = memberships
)
}
}

View File

@ -49,7 +49,7 @@ enum class Membership(val value: String) {
return listOf(INVITE, JOIN) return listOf(INVITE, JOIN)
} }
fun all(): List<Membership>{ fun all(): List<Membership> {
return values().asList() return values().asList()
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019 New Vector Ltd * Copyright 2020 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,12 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.riotx.features.home.room.detail.composer package im.vector.matrix.android.internal.query
import im.vector.riotx.core.platform.VectorViewModelAction import io.realm.RealmObject
import io.realm.RealmQuery
sealed class TextComposerAction : VectorViewModelAction { fun <T : RealmObject, E : Enum<E>> RealmQuery<T>.process(field: String, enums: List<Enum<E>>): RealmQuery<T> {
data class QueryUsers(val query: CharSequence?) : TextComposerAction() val lastEnumValue = enums.lastOrNull()
data class QueryRooms(val query: CharSequence?) : TextComposerAction() this.beginGroup()
data class QueryGroups(val query: CharSequence?) : TextComposerAction() for (enumValue in enums) {
this.equalTo(field, enumValue.name)
if (enumValue != lastEnumValue) {
this.or()
}
}
this.endGroup()
return this
} }

View File

@ -0,0 +1,47 @@
/*
* Copyright 2020 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.query
import im.vector.matrix.android.api.query.QueryStringValue
import io.realm.Case
import io.realm.RealmObject
import io.realm.RealmQuery
import timber.log.Timber
fun <T : RealmObject> RealmQuery<T>.process(field: String, queryStringValue: QueryStringValue): RealmQuery<T> {
when (queryStringValue) {
is QueryStringValue.NoCondition -> Timber.v("No condition to process")
is QueryStringValue.IsNotNull -> this.isNotNull(field)
is QueryStringValue.IsNull -> this.isNull(field)
is QueryStringValue.IsEmpty -> this.isEmpty(field)
is QueryStringValue.IsNotEmpty -> this.isNotEmpty(field)
is QueryStringValue.Equals -> {
this.equalTo(field, queryStringValue.string, queryStringValue.case.toRealmCase())
}
is QueryStringValue.Contains -> {
this.contains(field, queryStringValue.string, queryStringValue.case.toRealmCase())
}
}
return this
}
private fun QueryStringValue.Case.toRealmCase(): Case {
return when (this) {
QueryStringValue.Case.INSENSITIVE -> Case.INSENSITIVE
QueryStringValue.Case.SENSITIVE -> Case.SENSITIVE
}
}

View File

@ -20,11 +20,13 @@ import androidx.lifecycle.LiveData
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.group.Group import im.vector.matrix.android.api.session.group.Group
import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.group.GroupSummaryQueryParams
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
import im.vector.matrix.android.internal.database.model.GroupSummaryEntityFields import im.vector.matrix.android.internal.database.model.GroupSummaryEntityFields
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.query.process
import im.vector.matrix.android.internal.util.fetchCopyMap import im.vector.matrix.android.internal.util.fetchCopyMap
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
@ -43,22 +45,23 @@ internal class DefaultGroupService @Inject constructor(private val monarchy: Mon
) )
} }
override fun getGroupSummaries(): List<GroupSummary> { override fun getGroupSummaries(groupSummaryQueryParams: GroupSummaryQueryParams): List<GroupSummary> {
return monarchy.fetchAllMappedSync( return monarchy.fetchAllMappedSync(
{ groupSummariesQuery(it) }, { groupSummariesQuery(it, groupSummaryQueryParams) },
{ it.asDomain() } { it.asDomain() }
) )
} }
override fun getGroupSummariesLive(groupSummaryQueryParams: GroupSummaryQueryParams): LiveData<List<GroupSummary>> {
override fun getGroupSummariesLive(): LiveData<List<GroupSummary>> {
return monarchy.findAllMappedWithChanges( return monarchy.findAllMappedWithChanges(
{ groupSummariesQuery(it) }, { groupSummariesQuery(it, groupSummaryQueryParams) },
{ it.asDomain() } { it.asDomain() }
) )
} }
private fun groupSummariesQuery(realm: Realm): RealmQuery<GroupSummaryEntity> { private fun groupSummariesQuery(realm: Realm, queryParams: GroupSummaryQueryParams): RealmQuery<GroupSummaryEntity> {
return GroupSummaryEntity.where(realm).isNotEmpty(GroupSummaryEntityFields.DISPLAY_NAME) return GroupSummaryEntity.where(realm)
.process(GroupSummaryEntityFields.DISPLAY_NAME, queryParams.displayName)
.process(GroupSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
} }
} }

View File

@ -33,6 +33,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.findByAlias import im.vector.matrix.android.internal.database.query.findByAlias
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.query.process
import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask
import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.session.room.create.CreateRoomTask
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
@ -104,21 +105,9 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
private fun roomSummariesQuery(realm: Realm, queryParams: RoomSummaryQueryParams): RealmQuery<RoomSummaryEntity> { private fun roomSummariesQuery(realm: Realm, queryParams: RoomSummaryQueryParams): RealmQuery<RoomSummaryEntity> {
val query = RoomSummaryEntity.where(realm) val query = RoomSummaryEntity.where(realm)
if (queryParams.filterCanonicalAlias) { query.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName)
query.isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias)
} query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
if (queryParams.filterCanonicalAlias) {
query.isNotEmpty(RoomSummaryEntityFields.CANONICAL_ALIAS)
}
val lastMembership = queryParams.memberships.lastOrNull()
query.beginGroup()
for (membership in queryParams.memberships) {
query.equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, membership.name)
if (membership != lastMembership) {
query.or()
}
}
query.endGroup()
query.notEqualTo(RoomSummaryEntityFields.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name) query.notEqualTo(RoomSummaryEntityFields.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name)
return query return query
} }

View File

@ -22,12 +22,14 @@ import com.squareup.inject.assisted.AssistedInject
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.members.MembershipService import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams
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.RoomMember import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.RoomMemberEntity import im.vector.matrix.android.internal.database.model.RoomMemberEntity
import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields
import im.vector.matrix.android.internal.query.process
import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRoomTask import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRoomTask
@ -67,10 +69,10 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr
return roomMemberEntity?.asDomain() return roomMemberEntity?.asDomain()
} }
override fun getRoomMembers(memberships: List<Membership>): List<RoomMember> { override fun getRoomMembers(queryParams: RoomMemberQueryParams): List<RoomMember> {
return monarchy.fetchAllMappedSync( return monarchy.fetchAllMappedSync(
{ {
roomMembersQuery(it, memberships) roomMembersQuery(it, queryParams)
}, },
{ {
it.asDomain() it.asDomain()
@ -78,10 +80,10 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr
) )
} }
override fun getRoomMembersLive(memberships: List<Membership>): LiveData<List<RoomMember>> { override fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData<List<RoomMember>> {
return monarchy.findAllMappedWithChanges( return monarchy.findAllMappedWithChanges(
{ {
roomMembersQuery(it, memberships) roomMembersQuery(it, queryParams)
}, },
{ {
it.asDomain() it.asDomain()
@ -89,18 +91,10 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr
) )
} }
private fun roomMembersQuery(realm: Realm, memberships: List<Membership>): RealmQuery<RoomMemberEntity> { private fun roomMembersQuery(realm: Realm, queryParams: RoomMemberQueryParams): RealmQuery<RoomMemberEntity> {
val query = RoomMembers(realm, roomId).queryRoomMembersEvent() return RoomMembers(realm, roomId).queryRoomMembersEvent()
val lastMembership = memberships.lastOrNull() .process(RoomMemberEntityFields.MEMBERSHIP_STR, queryParams.memberships)
query.beginGroup() .process(RoomMemberEntityFields.DISPLAY_NAME, queryParams.displayName)
for (membership in memberships) {
query.equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, membership.name)
if (membership != lastMembership) {
query.or()
}
}
query.endGroup()
return query
} }
override fun getNumberOfJoinedMembers(): Int { override fun getNumberOfJoinedMembers(): Int {

View File

@ -21,7 +21,6 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.helper.deleteOnCascade import im.vector.matrix.android.internal.database.helper.deleteOnCascade
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.ChunkEntityFields import im.vector.matrix.android.internal.database.model.ChunkEntityFields
import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.awaitTransaction import im.vector.matrix.android.internal.util.awaitTransaction

View File

@ -21,8 +21,8 @@ import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.OnLifecycleEvent
import arrow.core.Option import arrow.core.Option
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
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.roomSummaryQueryParams
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.features.home.HomeRoomListDataSource import im.vector.riotx.features.home.HomeRoomListDataSource
import im.vector.riotx.features.home.group.ALL_COMMUNITIES_GROUP_ID import im.vector.riotx.features.home.group.ALL_COMMUNITIES_GROUP_ID
@ -66,7 +66,8 @@ class AppStateHandler @Inject constructor(
sessionDataSource.observe() sessionDataSource.observe()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.switchMap { .switchMap {
it.orNull()?.rx()?.liveRoomSummaries(RoomSummaryQueryParams()) val query = roomSummaryQueryParams {}
it.orNull()?.rx()?.liveRoomSummaries(query)
?: Observable.just(emptyList()) ?: Observable.just(emptyList())
} }
.throttleLast(300, TimeUnit.MILLISECONDS), .throttleLast(300, TimeUnit.MILLISECONDS),

View File

@ -37,10 +37,6 @@ class AutocompleteEmojiController @Inject constructor(
} }
} }
init {
fontProvider.addListener(fontProviderListener)
}
var listener: AutocompleteClickListener<String>? = null var listener: AutocompleteClickListener<String>? = null
override fun buildModels(data: List<EmojiItem>?) { override fun buildModels(data: List<EmojiItem>?) {
@ -71,6 +67,10 @@ class AutocompleteEmojiController @Inject constructor(
} }
} }
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
fontProvider.addListener(fontProviderListener)
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView) super.onDetachedFromRecyclerView(recyclerView)
fontProvider.removeListener(fontProviderListener) fontProvider.removeListener(fontProviderListener)

View File

@ -19,18 +19,19 @@ package im.vector.riotx.features.autocomplete.group
import android.content.Context import android.content.Context
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Success
import com.otaliastudios.autocomplete.RecyclerViewPresenter import com.otaliastudios.autocomplete.RecyclerViewPresenter
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.group.groupSummaryQueryParams
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import javax.inject.Inject import javax.inject.Inject
class AutocompleteGroupPresenter @Inject constructor(context: Context, class AutocompleteGroupPresenter @Inject constructor(context: Context,
private val controller: AutocompleteGroupController private val controller: AutocompleteGroupController,
private val session: Session
) : RecyclerViewPresenter<GroupSummary>(context), AutocompleteClickListener<GroupSummary> { ) : RecyclerViewPresenter<GroupSummary>(context), AutocompleteClickListener<GroupSummary> {
var callback: Callback? = null
init { init {
controller.listener = this controller.listener = this
} }
@ -46,16 +47,20 @@ class AutocompleteGroupPresenter @Inject constructor(context: Context,
} }
override fun onQuery(query: CharSequence?) { override fun onQuery(query: CharSequence?) {
callback?.onQueryGroups(query) val queryParams = groupSummaryQueryParams {
displayName = if (query.isNullOrBlank()) {
QueryStringValue.IsNotEmpty
} else {
QueryStringValue.Contains(query.toString(), QueryStringValue.Case.INSENSITIVE)
}
}
val groups = session.getGroupSummaries(queryParams)
.asSequence()
.sortedBy { it.displayName }
controller.setData(groups.toList())
} }
fun render(groups: Async<List<GroupSummary>>) { fun render(groups: Async<List<GroupSummary>>) {
if (groups is Success) { controller.setData(groups())
controller.setData(groups())
}
}
interface Callback {
fun onQueryGroups(query: CharSequence?)
} }
} }

View File

@ -21,20 +21,32 @@ import androidx.recyclerview.widget.RecyclerView
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.otaliastudios.autocomplete.RecyclerViewPresenter import com.otaliastudios.autocomplete.RecyclerViewPresenter
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import javax.inject.Inject
class AutocompleteMemberPresenter @Inject constructor(context: Context, class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
private val controller: AutocompleteMemberController @Assisted val roomId: String,
private val session: Session,
private val controller: AutocompleteMemberController
) : RecyclerViewPresenter<RoomMember>(context), AutocompleteClickListener<RoomMember> { ) : RecyclerViewPresenter<RoomMember>(context), AutocompleteClickListener<RoomMember> {
var callback: Callback? = null private val room = session.getRoom(roomId)!!
init { init {
controller.listener = this controller.listener = this
} }
@AssistedInject.Factory
interface Factory {
fun create(roomId: String): AutocompleteMemberPresenter
}
override fun instantiateAdapter(): RecyclerView.Adapter<*> { override fun instantiateAdapter(): RecyclerView.Adapter<*> {
// Also remove animation // Also remove animation
recyclerView?.itemAnimator = null recyclerView?.itemAnimator = null
@ -46,7 +58,18 @@ class AutocompleteMemberPresenter @Inject constructor(context: Context,
} }
override fun onQuery(query: CharSequence?) { override fun onQuery(query: CharSequence?) {
callback?.onQueryMembers(query) val queryParams = roomMemberQueryParams {
displayName = if (query.isNullOrBlank()) {
QueryStringValue.IsNotEmpty
} else {
QueryStringValue.Contains(query.toString(), QueryStringValue.Case.INSENSITIVE)
}
memberships = listOf(Membership.JOIN)
}
val members = room.getRoomMembers(queryParams)
.asSequence()
.sortedBy { it.displayName }
controller.setData(members.toList())
} }
fun render(members: Async<List<RoomMember>>) { fun render(members: Async<List<RoomMember>>) {
@ -54,8 +77,4 @@ class AutocompleteMemberPresenter @Inject constructor(context: Context,
controller.setData(members()) controller.setData(members())
} }
} }
interface Callback {
fun onQueryMembers(query: CharSequence?)
}
} }

View File

@ -24,12 +24,10 @@ import im.vector.riotx.features.autocomplete.autocompleteMatrixItem
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import javax.inject.Inject import javax.inject.Inject
class AutocompleteRoomController @Inject constructor() : TypedEpoxyController<List<RoomSummary>>() { class AutocompleteRoomController @Inject constructor(private val avatarRenderer: AvatarRenderer) : TypedEpoxyController<List<RoomSummary>>() {
var listener: AutocompleteClickListener<RoomSummary>? = null var listener: AutocompleteClickListener<RoomSummary>? = null
@Inject lateinit var avatarRenderer: AvatarRenderer
override fun buildModels(data: List<RoomSummary>?) { override fun buildModels(data: List<RoomSummary>?) {
if (data.isNullOrEmpty()) { if (data.isNullOrEmpty()) {
return return

View File

@ -18,19 +18,19 @@ package im.vector.riotx.features.autocomplete.room
import android.content.Context import android.content.Context
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Success
import com.otaliastudios.autocomplete.RecyclerViewPresenter import com.otaliastudios.autocomplete.RecyclerViewPresenter
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.Session
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.roomSummaryQueryParams
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import javax.inject.Inject import javax.inject.Inject
class AutocompleteRoomPresenter @Inject constructor(context: Context, class AutocompleteRoomPresenter @Inject constructor(context: Context,
private val controller: AutocompleteRoomController private val controller: AutocompleteRoomController,
private val session: Session
) : RecyclerViewPresenter<RoomSummary>(context), AutocompleteClickListener<RoomSummary> { ) : RecyclerViewPresenter<RoomSummary>(context), AutocompleteClickListener<RoomSummary> {
var callback: Callback? = null
init { init {
controller.listener = this controller.listener = this
} }
@ -46,16 +46,16 @@ class AutocompleteRoomPresenter @Inject constructor(context: Context,
} }
override fun onQuery(query: CharSequence?) { override fun onQuery(query: CharSequence?) {
callback?.onQueryRooms(query) val queryParams = roomSummaryQueryParams {
} canonicalAlias = if (query.isNullOrBlank()) {
QueryStringValue.IsNotNull
fun render(rooms: Async<List<RoomSummary>>) { } else {
if (rooms is Success) { QueryStringValue.Contains(query.toString(), QueryStringValue.Case.INSENSITIVE)
controller.setData(rooms()) }
} }
} val rooms = session.getRoomSummaries(queryParams)
.asSequence()
interface Callback { .sortedBy { it.displayName }
fun onQueryRooms(query: CharSequence?) controller.setData(rooms.toList())
} }
} }

View File

@ -24,7 +24,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
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.query.QueryStringValue
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.group.groupSummaryQueryParams
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
@ -96,6 +98,10 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
} }
private fun observeGroupSummaries() { private fun observeGroupSummaries() {
val groupSummariesQueryParams = groupSummaryQueryParams {
memberships = listOf(Membership.JOIN)
displayName = QueryStringValue.IsNotEmpty
}
Observable.combineLatest<GroupSummary, List<GroupSummary>, List<GroupSummary>>( Observable.combineLatest<GroupSummary, List<GroupSummary>, List<GroupSummary>>(
session session
.rx() .rx()
@ -109,9 +115,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
}, },
session session
.rx() .rx()
.liveGroupSummaries() .liveGroupSummaries(groupSummariesQueryParams),
// Keep only joined groups. Group invitations will be managed later
.map { it.filter { groupSummary -> groupSummary.membership == Membership.JOIN } },
BiFunction { allCommunityGroup, communityGroups -> BiFunction { allCommunityGroup, communityGroups ->
listOf(allCommunityGroup) + communityGroups listOf(allCommunityGroup) + communityGroups
} }

View File

@ -24,6 +24,8 @@ import android.widget.EditText
import com.otaliastudios.autocomplete.Autocomplete import com.otaliastudios.autocomplete.Autocomplete
import com.otaliastudios.autocomplete.AutocompleteCallback import com.otaliastudios.autocomplete.AutocompleteCallback
import com.otaliastudios.autocomplete.CharPolicy import com.otaliastudios.autocomplete.CharPolicy
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.RoomMember import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
@ -40,20 +42,25 @@ import im.vector.riotx.features.autocomplete.member.AutocompleteMemberPresenter
import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter
import im.vector.riotx.features.command.Command import im.vector.riotx.features.command.Command
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.composer.TextComposerViewState
import im.vector.riotx.features.html.PillImageSpan import im.vector.riotx.features.html.PillImageSpan
import im.vector.riotx.features.themes.ThemeUtils import im.vector.riotx.features.themes.ThemeUtils
import javax.inject.Inject
class AutoCompleter @Inject constructor( class AutoCompleter @AssistedInject constructor(
@Assisted val roomId: String,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val commandAutocompletePolicy: CommandAutocompletePolicy, private val commandAutocompletePolicy: CommandAutocompletePolicy,
private val autocompleteCommandPresenter: AutocompleteCommandPresenter, private val autocompleteCommandPresenter: AutocompleteCommandPresenter,
private val autocompleteMemberPresenter: AutocompleteMemberPresenter, private val autocompleteMemberPresenter: AutocompleteMemberPresenter.Factory,
private val autocompleteRoomPresenter: AutocompleteRoomPresenter, private val autocompleteRoomPresenter: AutocompleteRoomPresenter,
private val autocompleteGroupPresenter: AutocompleteGroupPresenter, private val autocompleteGroupPresenter: AutocompleteGroupPresenter,
private val autocompleteEmojiPresenter: AutocompleteEmojiPresenter private val autocompleteEmojiPresenter: AutocompleteEmojiPresenter
) { ) {
@AssistedInject.Factory
interface Factory {
fun create(roomId: String): AutoCompleter
}
private lateinit var editText: EditText private lateinit var editText: EditText
fun enterSpecialMode() { fun enterSpecialMode() {
@ -68,22 +75,14 @@ class AutoCompleter @Inject constructor(
GlideApp.with(editText) GlideApp.with(editText)
} }
fun setup(editText: EditText, listener: AutoCompleterListener) { fun setup(editText: EditText) {
this.editText = editText this.editText = editText
val backgroundDrawable = ColorDrawable(ThemeUtils.getColor(editText.context, R.attr.riotx_background)) val backgroundDrawable = ColorDrawable(ThemeUtils.getColor(editText.context, R.attr.riotx_background))
setupCommands(backgroundDrawable, editText) setupCommands(backgroundDrawable, editText)
setupUsers(backgroundDrawable, editText, listener) setupMembers(backgroundDrawable, editText)
setupRooms(backgroundDrawable, editText, listener) setupGroups(backgroundDrawable, editText)
setupGroups(backgroundDrawable, editText, listener)
setupEmojis(backgroundDrawable, editText) setupEmojis(backgroundDrawable, editText)
} setupRooms(backgroundDrawable, editText)
fun render(state: TextComposerViewState) {
autocompleteMemberPresenter.render(state.asyncMembers)
autocompleteRoomPresenter.render(state.asyncRooms)
autocompleteGroupPresenter.render(state.asyncGroups)
} }
private fun setupCommands(backgroundDrawable: Drawable, editText: EditText) { private fun setupCommands(backgroundDrawable: Drawable, editText: EditText) {
@ -107,11 +106,11 @@ class AutoCompleter @Inject constructor(
.build() .build()
} }
private fun setupUsers(backgroundDrawable: ColorDrawable, editText: EditText, listener: AutocompleteMemberPresenter.Callback) { private fun setupMembers(backgroundDrawable: ColorDrawable, editText: EditText) {
autocompleteMemberPresenter.callback = listener val membersPresenter = autocompleteMemberPresenter.create(roomId)
Autocomplete.on<RoomMember>(editText) Autocomplete.on<RoomMember>(editText)
.with(CharPolicy('@', true)) .with(CharPolicy('@', true))
.with(autocompleteMemberPresenter) .with(membersPresenter)
.with(ELEVATION) .with(ELEVATION)
.with(backgroundDrawable) .with(backgroundDrawable)
.with(object : AutocompleteCallback<RoomMember> { .with(object : AutocompleteCallback<RoomMember> {
@ -126,8 +125,7 @@ class AutoCompleter @Inject constructor(
.build() .build()
} }
private fun setupRooms(backgroundDrawable: ColorDrawable, editText: EditText, listener: AutocompleteRoomPresenter.Callback) { private fun setupRooms(backgroundDrawable: ColorDrawable, editText: EditText) {
autocompleteRoomPresenter.callback = listener
Autocomplete.on<RoomSummary>(editText) Autocomplete.on<RoomSummary>(editText)
.with(CharPolicy('#', true)) .with(CharPolicy('#', true))
.with(autocompleteRoomPresenter) .with(autocompleteRoomPresenter)
@ -145,8 +143,7 @@ class AutoCompleter @Inject constructor(
.build() .build()
} }
private fun setupGroups(backgroundDrawable: ColorDrawable, editText: EditText, listener: AutocompleteGroupPresenter.Callback) { private fun setupGroups(backgroundDrawable: ColorDrawable, editText: EditText) {
autocompleteGroupPresenter.callback = listener
Autocomplete.on<GroupSummary>(editText) Autocomplete.on<GroupSummary>(editText)
.with(CharPolicy('+', true)) .with(CharPolicy('+', true))
.with(autocompleteGroupPresenter) .with(autocompleteGroupPresenter)
@ -226,11 +223,6 @@ class AutoCompleter @Inject constructor(
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
} }
interface AutoCompleterListener :
AutocompleteMemberPresenter.Callback,
AutocompleteRoomPresenter.Callback,
AutocompleteGroupPresenter.Callback
companion object { companion object {
private const val ELEVATION = 6f private const val ELEVATION = 6f
} }

View File

@ -78,10 +78,7 @@ import im.vector.riotx.features.attachments.ContactAttachment
import im.vector.riotx.features.command.Command import im.vector.riotx.features.command.Command
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.getColorFromUserId import im.vector.riotx.features.home.getColorFromUserId
import im.vector.riotx.features.home.room.detail.composer.TextComposerAction
import im.vector.riotx.features.home.room.detail.composer.TextComposerView import im.vector.riotx.features.home.room.detail.composer.TextComposerView
import im.vector.riotx.features.home.room.detail.composer.TextComposerViewModel
import im.vector.riotx.features.home.room.detail.composer.TextComposerViewState
import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.home.room.detail.timeline.action.EventSharedAction import im.vector.riotx.features.home.room.detail.timeline.action.EventSharedAction
@ -127,17 +124,15 @@ class RoomDetailFragment @Inject constructor(
private val session: Session, private val session: Session,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val timelineEventController: TimelineEventController, private val timelineEventController: TimelineEventController,
private val autoCompleter: AutoCompleter, autoCompleterFactory: AutoCompleter.Factory,
private val permalinkHandler: PermalinkHandler, private val permalinkHandler: PermalinkHandler,
private val notificationDrawerManager: NotificationDrawerManager, private val notificationDrawerManager: NotificationDrawerManager,
val roomDetailViewModelFactory: RoomDetailViewModel.Factory, val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
val textComposerViewModelFactory: TextComposerViewModel.Factory,
private val eventHtmlRenderer: EventHtmlRenderer, private val eventHtmlRenderer: EventHtmlRenderer,
private val vectorPreferences: VectorPreferences private val vectorPreferences: VectorPreferences
) : ) :
VectorBaseFragment(), VectorBaseFragment(),
TimelineEventController.Callback, TimelineEventController.Callback,
AutoCompleter.AutoCompleterListener,
VectorInviteView.Callback, VectorInviteView.Callback,
JumpToReadMarkerView.Callback, JumpToReadMarkerView.Callback,
AttachmentTypeSelectorView.Callback, AttachmentTypeSelectorView.Callback,
@ -167,9 +162,10 @@ class RoomDetailFragment @Inject constructor(
GlideApp.with(this) GlideApp.with(this)
} }
private val autoCompleter: AutoCompleter by lazy {
autoCompleterFactory.create(roomDetailArgs.roomId)
}
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel() private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
private val textComposerViewModel: TextComposerViewModel by fragmentViewModel()
private val debouncer = Debouncer(createUIHandler()) private val debouncer = Debouncer(createUIHandler())
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
@ -206,7 +202,6 @@ class RoomDetailFragment @Inject constructor(
setupJumpToBottomView() setupJumpToBottomView()
roomDetailViewModel.subscribe { renderState(it) } roomDetailViewModel.subscribe { renderState(it) }
textComposerViewModel.subscribe { renderTextComposerState(it) }
roomDetailViewModel.sendMessageResultLiveData.observeEvent(viewLifecycleOwner) { renderSendMessageResult(it) } roomDetailViewModel.sendMessageResultLiveData.observeEvent(viewLifecycleOwner) { renderSendMessageResult(it) }
roomDetailViewModel.nonBlockingPopAlert.observeEvent(this) { pair -> roomDetailViewModel.nonBlockingPopAlert.observeEvent(this) { pair ->
@ -250,9 +245,9 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode) { mode -> roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode) { mode ->
when (mode) { when (mode) {
is SendMode.REGULAR -> renderRegularMode(mode.text) is SendMode.REGULAR -> renderRegularMode(mode.text)
is SendMode.EDIT -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text) is SendMode.EDIT -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text)
is SendMode.QUOTE -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text) is SendMode.QUOTE -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text)
is SendMode.REPLY -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text) is SendMode.REPLY -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text)
} }
} }
@ -279,9 +274,9 @@ class RoomDetailFragment @Inject constructor(
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null) { if (savedInstanceState == null) {
when (val sharedData = roomDetailArgs.sharedData) { when (val sharedData = roomDetailArgs.sharedData) {
is SharedData.Text -> roomDetailViewModel.handle(RoomDetailAction.SendMessage(sharedData.text, false)) is SharedData.Text -> roomDetailViewModel.handle(RoomDetailAction.SendMessage(sharedData.text, false))
is SharedData.Attachments -> roomDetailViewModel.handle(RoomDetailAction.SendMedia(sharedData.attachmentData)) is SharedData.Attachments -> roomDetailViewModel.handle(RoomDetailAction.SendMedia(sharedData.attachmentData))
null -> Timber.v("No share data to process") null -> Timber.v("No share data to process")
} }
} }
} }
@ -485,7 +480,7 @@ class RoomDetailFragment @Inject constructor(
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
when (newState) { when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> { RecyclerView.SCROLL_STATE_IDLE -> {
updateJumpToBottomViewVisibility() updateJumpToBottomViewVisibility()
} }
RecyclerView.SCROLL_STATE_DRAGGING, RecyclerView.SCROLL_STATE_DRAGGING,
@ -512,7 +507,7 @@ class RoomDetailFragment @Inject constructor(
is MessageTextItem -> { is MessageTextItem -> {
return (model as AbsMessageItem).attributes.informationData.sendState == SendState.SYNCED return (model as AbsMessageItem).attributes.informationData.sendState == SendState.SYNCED
} }
else -> false else -> false
} }
} }
} }
@ -527,9 +522,9 @@ class RoomDetailFragment @Inject constructor(
withState(roomDetailViewModel) { withState(roomDetailViewModel) {
val showJumpToUnreadBanner = when (it.unreadState) { val showJumpToUnreadBanner = when (it.unreadState) {
UnreadState.Unknown, UnreadState.Unknown,
UnreadState.HasNoUnread -> false UnreadState.HasNoUnread -> false
is UnreadState.ReadMarkerNotLoaded -> true is UnreadState.ReadMarkerNotLoaded -> true
is UnreadState.HasUnread -> { is UnreadState.HasUnread -> {
if (it.canShowJumpToReadMarker) { if (it.canShowJumpToReadMarker) {
val lastVisibleItem = layoutManager.findLastVisibleItemPosition() val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
val positionOfReadMarker = timelineEventController.getPositionOfReadMarker() val positionOfReadMarker = timelineEventController.getPositionOfReadMarker()
@ -560,7 +555,7 @@ class RoomDetailFragment @Inject constructor(
} }
private fun setupComposer() { private fun setupComposer() {
autoCompleter.setup(composerLayout.composerEditText, this) autoCompleter.setup(composerLayout.composerEditText)
composerLayout.callback = object : TextComposerView.Callback { composerLayout.callback = object : TextComposerView.Callback {
override fun onAddAttachment() { override fun onAddAttachment() {
if (!::attachmentTypeSelector.isInitialized) { if (!::attachmentTypeSelector.isInitialized) {
@ -656,10 +651,6 @@ class RoomDetailFragment @Inject constructor(
} }
} }
private fun renderTextComposerState(state: TextComposerViewState) {
autoCompleter.render(state)
}
private fun renderTombstoneEventHandling(async: Async<String>) { private fun renderTombstoneEventHandling(async: Async<String>) {
when (async) { when (async) {
is Loading -> { is Loading -> {
@ -672,7 +663,7 @@ class RoomDetailFragment @Inject constructor(
navigator.openRoom(vectorBaseActivity, async()) navigator.openRoom(vectorBaseActivity, async())
vectorBaseActivity.finish() vectorBaseActivity.finish()
} }
is Fail -> { is Fail -> {
vectorBaseActivity.hideWaitingView() vectorBaseActivity.hideWaitingView()
vectorBaseActivity.toast(errorFormatter.toHumanReadable(async.error)) vectorBaseActivity.toast(errorFormatter.toHumanReadable(async.error))
} }
@ -681,23 +672,23 @@ class RoomDetailFragment @Inject constructor(
private fun renderSendMessageResult(sendMessageResult: SendMessageResult) { private fun renderSendMessageResult(sendMessageResult: SendMessageResult) {
when (sendMessageResult) { when (sendMessageResult) {
is SendMessageResult.MessageSent -> { is SendMessageResult.MessageSent -> {
updateComposerText("") updateComposerText("")
} }
is SendMessageResult.SlashCommandHandled -> { is SendMessageResult.SlashCommandHandled -> {
sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) } sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) }
updateComposerText("") updateComposerText("")
} }
is SendMessageResult.SlashCommandError -> { is SendMessageResult.SlashCommandError -> {
displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command)) displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command))
} }
is SendMessageResult.SlashCommandUnknown -> { is SendMessageResult.SlashCommandUnknown -> {
displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command)) displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command))
} }
is SendMessageResult.SlashCommandResultOk -> { is SendMessageResult.SlashCommandResultOk -> {
updateComposerText("") updateComposerText("")
} }
is SendMessageResult.SlashCommandResultError -> { is SendMessageResult.SlashCommandResultError -> {
displayCommandError(sendMessageResult.throwable.localizedMessage) displayCommandError(sendMessageResult.throwable.localizedMessage)
} }
is SendMessageResult.SlashCommandNotImplemented -> { is SendMessageResult.SlashCommandNotImplemented -> {
@ -735,7 +726,7 @@ class RoomDetailFragment @Inject constructor(
private fun displayRoomDetailActionResult(result: Async<RoomDetailAction>) { private fun displayRoomDetailActionResult(result: Async<RoomDetailAction>) {
when (result) { when (result) {
is Fail -> { is Fail -> {
AlertDialog.Builder(requireActivity()) AlertDialog.Builder(requireActivity())
.setTitle(R.string.dialog_title_error) .setTitle(R.string.dialog_title_error)
.setMessage(errorFormatter.toHumanReadable(result.error)) .setMessage(errorFormatter.toHumanReadable(result.error))
@ -746,7 +737,7 @@ class RoomDetailFragment @Inject constructor(
when (val data = result.invoke()) { when (val data = result.invoke()) {
is RoomDetailAction.ReportContent -> { is RoomDetailAction.ReportContent -> {
when { when {
data.spam -> { data.spam -> {
AlertDialog.Builder(requireActivity()) AlertDialog.Builder(requireActivity())
.setTitle(R.string.content_reported_as_spam_title) .setTitle(R.string.content_reported_as_spam_title)
.setMessage(R.string.content_reported_as_spam_content) .setMessage(R.string.content_reported_as_spam_content)
@ -768,7 +759,7 @@ class RoomDetailFragment @Inject constructor(
.show() .show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE) .withColoredButton(DialogInterface.BUTTON_NEGATIVE)
} }
else -> { else -> {
AlertDialog.Builder(requireActivity()) AlertDialog.Builder(requireActivity())
.setTitle(R.string.content_reported_title) .setTitle(R.string.content_reported_title)
.setMessage(R.string.content_reported_content) .setMessage(R.string.content_reported_content)
@ -881,14 +872,14 @@ class RoomDetailFragment @Inject constructor(
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
if (allGranted(grantResults)) { if (allGranted(grantResults)) {
when (requestCode) { when (requestCode) {
PERMISSION_REQUEST_CODE_DOWNLOAD_FILE -> { PERMISSION_REQUEST_CODE_DOWNLOAD_FILE -> {
val action = roomDetailViewModel.pendingAction val action = roomDetailViewModel.pendingAction
if (action != null) { if (action != null) {
roomDetailViewModel.pendingAction = null roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(action) roomDetailViewModel.handle(action)
} }
} }
PERMISSION_REQUEST_CODE_INCOMING_URI -> { PERMISSION_REQUEST_CODE_INCOMING_URI -> {
val pendingUri = roomDetailViewModel.pendingUri val pendingUri = roomDetailViewModel.pendingUri
if (pendingUri != null) { if (pendingUri != null) {
roomDetailViewModel.pendingUri = null roomDetailViewModel.pendingUri = null
@ -984,43 +975,25 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.EnterTrackingUnreadMessagesState) roomDetailViewModel.handle(RoomDetailAction.EnterTrackingUnreadMessagesState)
} }
// AutocompleteMemberPresenter.Callback
override fun onQueryMembers(query: CharSequence?) {
textComposerViewModel.handle(TextComposerAction.QueryUsers(query))
}
// AutocompleteRoomPresenter.Callback
override fun onQueryRooms(query: CharSequence?) {
textComposerViewModel.handle(TextComposerAction.QueryRooms(query))
}
// AutocompleteGroupPresenter.Callback
override fun onQueryGroups(query: CharSequence?) {
textComposerViewModel.handle(TextComposerAction.QueryGroups(query))
}
private fun handleActions(action: EventSharedAction) { private fun handleActions(action: EventSharedAction) {
when (action) { when (action) {
is EventSharedAction.AddReaction -> { is EventSharedAction.AddReaction -> {
startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE) startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE)
} }
is EventSharedAction.ViewReactions -> { is EventSharedAction.ViewReactions -> {
ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData) ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS") .show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
} }
is EventSharedAction.Copy -> { is EventSharedAction.Copy -> {
// I need info about the current selected message :/ // I need info about the current selected message :/
copyToClipboard(requireContext(), action.content, false) copyToClipboard(requireContext(), action.content, false)
val msg = requireContext().getString(R.string.copied_to_clipboard) val msg = requireContext().getString(R.string.copied_to_clipboard)
showSnackWithMessage(msg, Snackbar.LENGTH_SHORT) showSnackWithMessage(msg, Snackbar.LENGTH_SHORT)
} }
is EventSharedAction.Delete -> { is EventSharedAction.Delete -> {
roomDetailViewModel.handle(RoomDetailAction.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason))) roomDetailViewModel.handle(RoomDetailAction.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason)))
} }
is EventSharedAction.Share -> { is EventSharedAction.Share -> {
// TODO current data communication is too limited // TODO current data communication is too limited
// Need to now the media type // Need to now the media type
// TODO bad, just POC // TODO bad, just POC
@ -1048,10 +1021,10 @@ class RoomDetailFragment @Inject constructor(
} }
) )
} }
is EventSharedAction.ViewEditHistory -> { is EventSharedAction.ViewEditHistory -> {
onEditedDecorationClicked(action.messageInformationData) onEditedDecorationClicked(action.messageInformationData)
} }
is EventSharedAction.ViewSource -> { is EventSharedAction.ViewSource -> {
val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null) val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null)
view.findViewById<TextView>(R.id.event_content_text_view)?.let { view.findViewById<TextView>(R.id.event_content_text_view)?.let {
it.text = action.content it.text = action.content
@ -1062,7 +1035,7 @@ class RoomDetailFragment @Inject constructor(
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
} }
is EventSharedAction.ViewDecryptedSource -> { is EventSharedAction.ViewDecryptedSource -> {
val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null) val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null)
view.findViewById<TextView>(R.id.event_content_text_view)?.let { view.findViewById<TextView>(R.id.event_content_text_view)?.let {
it.text = action.content it.text = action.content
@ -1073,31 +1046,31 @@ class RoomDetailFragment @Inject constructor(
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
} }
is EventSharedAction.QuickReact -> { is EventSharedAction.QuickReact -> {
// eventId,ClickedOn,Add // eventId,ClickedOn,Add
roomDetailViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add)) roomDetailViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add))
} }
is EventSharedAction.Edit -> { is EventSharedAction.Edit -> {
roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, composerLayout.text.toString())) roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, composerLayout.text.toString()))
} }
is EventSharedAction.Quote -> { is EventSharedAction.Quote -> {
roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, composerLayout.text.toString())) roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, composerLayout.text.toString()))
} }
is EventSharedAction.Reply -> { is EventSharedAction.Reply -> {
roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString())) roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString()))
} }
is EventSharedAction.CopyPermalink -> { is EventSharedAction.CopyPermalink -> {
val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId) val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId)
copyToClipboard(requireContext(), permalink, false) copyToClipboard(requireContext(), permalink, false)
showSnackWithMessage(requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) showSnackWithMessage(requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
} }
is EventSharedAction.Resend -> { is EventSharedAction.Resend -> {
roomDetailViewModel.handle(RoomDetailAction.ResendMessage(action.eventId)) roomDetailViewModel.handle(RoomDetailAction.ResendMessage(action.eventId))
} }
is EventSharedAction.Remove -> { is EventSharedAction.Remove -> {
roomDetailViewModel.handle(RoomDetailAction.RemoveFailedEcho(action.eventId)) roomDetailViewModel.handle(RoomDetailAction.RemoveFailedEcho(action.eventId))
} }
is EventSharedAction.ReportContentSpam -> { is EventSharedAction.ReportContentSpam -> {
roomDetailViewModel.handle(RoomDetailAction.ReportContent( roomDetailViewModel.handle(RoomDetailAction.ReportContent(
action.eventId, action.senderId, "This message is spam", spam = true)) action.eventId, action.senderId, "This message is spam", spam = true))
} }
@ -1105,19 +1078,19 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.ReportContent( roomDetailViewModel.handle(RoomDetailAction.ReportContent(
action.eventId, action.senderId, "This message is inappropriate", inappropriate = true)) action.eventId, action.senderId, "This message is inappropriate", inappropriate = true))
} }
is EventSharedAction.ReportContentCustom -> { is EventSharedAction.ReportContentCustom -> {
promptReasonToReportContent(action) promptReasonToReportContent(action)
} }
is EventSharedAction.IgnoreUser -> { is EventSharedAction.IgnoreUser -> {
roomDetailViewModel.handle(RoomDetailAction.IgnoreUser(action.senderId)) roomDetailViewModel.handle(RoomDetailAction.IgnoreUser(action.senderId))
} }
is EventSharedAction.OnUrlClicked -> { is EventSharedAction.OnUrlClicked -> {
onUrlClicked(action.url) onUrlClicked(action.url)
} }
is EventSharedAction.OnUrlLongClicked -> { is EventSharedAction.OnUrlLongClicked -> {
onUrlLongClicked(action.url) onUrlLongClicked(action.url)
} }
else -> { else -> {
Toast.makeText(context, "Action $action is not implemented yet", Toast.LENGTH_LONG).show() Toast.makeText(context, "Action $action is not implemented yet", Toast.LENGTH_LONG).show()
} }
} }
@ -1225,10 +1198,10 @@ class RoomDetailFragment @Inject constructor(
private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) { private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) {
when (type) { when (type) {
AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera() AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera()
AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile() AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile()
AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery() AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery()
AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio() AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio()
AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact() AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact()
AttachmentTypeSelectorView.Type.STICKER -> vectorBaseActivity.notImplemented("Adding stickers") AttachmentTypeSelectorView.Type.STICKER -> vectorBaseActivity.notImplemented("Adding stickers")
} }

View File

@ -1,158 +0,0 @@
/*
* 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.riotx.features.home.room.detail.composer
import arrow.core.Option
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.room.detail.RoomDetailFragment
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import java.util.concurrent.TimeUnit
typealias AutocompleteQuery = CharSequence
class TextComposerViewModel @AssistedInject constructor(@Assisted initialState: TextComposerViewState,
private val session: Session
) : VectorViewModel<TextComposerViewState, TextComposerAction>(initialState) {
private val room = session.getRoom(initialState.roomId)!!
private val usersQueryObservable = BehaviorRelay.create<Option<AutocompleteQuery>>()
private val roomsQueryObservable = BehaviorRelay.create<Option<AutocompleteQuery>>()
private val groupsQueryObservable = BehaviorRelay.create<Option<AutocompleteQuery>>()
@AssistedInject.Factory
interface Factory {
fun create(initialState: TextComposerViewState): TextComposerViewModel
}
companion object : MvRxViewModelFactory<TextComposerViewModel, TextComposerViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel? {
val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.textComposerViewModelFactory.create(state)
}
}
init {
observeUsersQuery()
observeRoomsQuery()
observeGroupsQuery()
}
override fun handle(action: TextComposerAction) {
when (action) {
is TextComposerAction.QueryUsers -> handleQueryUsers(action)
is TextComposerAction.QueryRooms -> handleQueryRooms(action)
is TextComposerAction.QueryGroups -> handleQueryGroups(action)
}
}
private fun handleQueryUsers(action: TextComposerAction.QueryUsers) {
val query = Option.fromNullable(action.query)
usersQueryObservable.accept(query)
}
private fun handleQueryRooms(action: TextComposerAction.QueryRooms) {
val query = Option.fromNullable(action.query)
roomsQueryObservable.accept(query)
}
private fun handleQueryGroups(action: TextComposerAction.QueryGroups) {
val query = Option.fromNullable(action.query)
groupsQueryObservable.accept(query)
}
private fun observeUsersQuery() {
Observable.combineLatest<List<RoomMember>, Option<AutocompleteQuery>, List<RoomMember>>(
room.rx().liveRoomMembers(Membership.activeMemberships()),
usersQueryObservable.throttleLast(300, TimeUnit.MILLISECONDS),
BiFunction { roomMembers, query ->
val filter = query.orNull()
if (filter.isNullOrBlank()) {
roomMembers
} else {
roomMembers.filter {
it.displayName?.contains(filter, ignoreCase = true) ?: false
}
}
.sortedBy { it.displayName }
}
).execute { async ->
copy(
asyncMembers = async
)
}
}
private fun observeRoomsQuery() {
Observable.combineLatest<List<RoomSummary>, Option<AutocompleteQuery>, List<RoomSummary>>(
session.rx().liveRoomSummaries(RoomSummaryQueryParams(filterCanonicalAlias = true)),
roomsQueryObservable.throttleLast(300, TimeUnit.MILLISECONDS),
BiFunction { roomSummaries, query ->
val filter = query.orNull() ?: ""
// Keep only room with a canonical alias
roomSummaries
.filter {
it.canonicalAlias?.contains(filter, ignoreCase = true) == true
}
.sortedBy { it.displayName }
}
).execute { async ->
copy(
asyncRooms = async
)
}
}
private fun observeGroupsQuery() {
Observable.combineLatest<List<GroupSummary>, Option<AutocompleteQuery>, List<GroupSummary>>(
session.rx().liveGroupSummaries(),
groupsQueryObservable.throttleLast(300, TimeUnit.MILLISECONDS),
BiFunction { groupSummaries, query ->
val filter = query.orNull()
if (filter.isNullOrBlank()) {
groupSummaries
} else {
groupSummaries
.filter {
it.groupId.contains(filter, ignoreCase = true)
}
}
.sortedBy { it.displayName }
}
).execute { async ->
copy(
asyncGroups = async
)
}
}
}

View File

@ -1,34 +0,0 @@
/*
* 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.riotx.features.home.room.detail.composer
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
data class TextComposerViewState(val roomId: String,
val asyncMembers: Async<List<RoomMember>> = Uninitialized,
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
val asyncGroups: Async<List<GroupSummary>> = Uninitialized
) : MvRxState {
constructor(args: RoomDetailArgs) : this(roomId = args.roomId)
}

View File

@ -18,7 +18,6 @@ package im.vector.riotx.features.reactions.data
import android.content.res.Resources import android.content.res.Resources
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenScope
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -42,6 +41,7 @@ class EmojiDataSource @Inject constructor(
// First add emojis with name matching query, sorted by name // First add emojis with name matching query, sorted by name
return (rawData.emojis.values return (rawData.emojis.values
.asSequence()
.filter { emojiItem -> .filter { emojiItem ->
emojiItem.name.contains(query, true) emojiItem.name.contains(query, true)
} }
@ -56,6 +56,7 @@ class EmojiDataSource @Inject constructor(
.sortedBy { it.name }) .sortedBy { it.name })
// and ensure they will not be present twice // and ensure they will not be present twice
.distinct() .distinct()
.toList()
} }
fun getQuickReactions(): List<EmojiItem> { fun getQuickReactions(): List<EmojiItem> {

View File

@ -24,12 +24,12 @@ 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.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
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.roomdirectory.PublicRoomsFilter import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsFilter
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.extensions.postLiveEvent
@ -80,9 +80,12 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
} }
private fun observeJoinedRooms() { private fun observeJoinedRooms() {
val queryParams = roomSummaryQueryParams {
memberships = listOf(Membership.JOIN)
}
session session
.rx() .rx()
.liveRoomSummaries(RoomSummaryQueryParams(memberships = listOf(Membership.JOIN))) .liveRoomSummaries(queryParams)
.subscribe { list -> .subscribe { list ->
val joinedRoomIds = list val joinedRoomIds = list
?.map { it.roomId } ?.map { it.roomId }
@ -105,9 +108,9 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
override fun handle(action: RoomDirectoryAction) { override fun handle(action: RoomDirectoryAction) {
when (action) { when (action) {
is RoomDirectoryAction.SetRoomDirectoryData -> setRoomDirectoryData(action) is RoomDirectoryAction.SetRoomDirectoryData -> setRoomDirectoryData(action)
is RoomDirectoryAction.FilterWith -> filterWith(action) is RoomDirectoryAction.FilterWith -> filterWith(action)
RoomDirectoryAction.LoadMore -> loadMore() RoomDirectoryAction.LoadMore -> loadMore()
is RoomDirectoryAction.JoinRoom -> joinRoom(action) is RoomDirectoryAction.JoinRoom -> joinRoom(action)
} }
} }

View File

@ -23,8 +23,8 @@ 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.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
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.roomSummaryQueryParams
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.roomdirectory.JoinState import im.vector.riotx.features.roomdirectory.JoinState
@ -54,9 +54,12 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R
} }
private fun observeJoinedRooms() { private fun observeJoinedRooms() {
val queryParams = roomSummaryQueryParams {
memberships = listOf(Membership.JOIN)
}
session session
.rx() .rx()
.liveRoomSummaries(RoomSummaryQueryParams(memberships = listOf(Membership.JOIN))) .liveRoomSummaries(queryParams)
.subscribe { list -> .subscribe { list ->
withState { state -> withState { state ->
val isRoomJoined = list val isRoomJoined = list

View File

@ -398,7 +398,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
fun getNotificationRingTone(): Uri? { fun getNotificationRingTone(): Uri? {
val url = defaultPrefs.getString(SETTINGS_NOTIFICATION_RINGTONE_PREFERENCE_KEY, null) val url = defaultPrefs.getString(SETTINGS_NOTIFICATION_RINGTONE_PREFERENCE_KEY, null)
// the user selects "None" // the user selects "NoCondition"
if (url == "") { if (url == "") {
return null return null
} }
@ -425,7 +425,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
/** /**
* Provide the notification ringtone filename * Provide the notification ringtone filename
* *
* @return the filename or null if "None" is selected * @return the filename or null if "NoCondition" is selected
*/ */
fun getNotificationRingToneName(): String? { fun getNotificationRingToneName(): String? {
val toneUri = getNotificationRingTone() ?: return null val toneUri = getNotificationRingTone() ?: return null

View File

@ -22,7 +22,7 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
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.session.room.RoomSummaryQueryParams import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.ActiveSessionDataSource import im.vector.riotx.ActiveSessionDataSource
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
@ -60,10 +60,11 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
} }
private fun observeRoomSummaries() { private fun observeRoomSummaries() {
val queryParams = roomSummaryQueryParams()
sessionObservableStore.observe() sessionObservableStore.observe()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.switchMap { .switchMap {
it.orNull()?.rx()?.liveRoomSummaries(RoomSummaryQueryParams()) it.orNull()?.rx()?.liveRoomSummaries(queryParams)
?: Observable.just(emptyList()) ?: Observable.just(emptyList())
} }
.throttleLast(300, TimeUnit.MILLISECONDS) .throttleLast(300, TimeUnit.MILLISECONDS)