Merge pull request #570 from vector-im/feature/left_group

Handle left group from sync
This commit is contained in:
Benoit Marty 2019-09-20 17:44:13 +02:00 committed by GitHub
commit 5651ea515b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 95 additions and 40 deletions

View file

@ -15,6 +15,7 @@ Other changes:
Bugfix:
- Fix characters erased from the Search field when the result are coming (#545)
- "No connection" banner was displayed by mistake
- Leaving community (from another client) has no effect on RiotX (#497)
Translations:
-

View file

@ -16,12 +16,15 @@
package im.vector.matrix.android.api.session.group.model
import im.vector.matrix.android.api.session.room.model.Membership
/**
* This class holds some data of a group.
* It can be retrieved through [im.vector.matrix.android.api.session.group.GroupService]
*/
data class GroupSummary(
val groupId: String,
val membership: Membership,
val displayName: String = "",
val shortDescription: String = "",
val avatarUrl: String = "",

View file

@ -22,14 +22,15 @@ import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
internal object GroupSummaryMapper {
fun map(roomSummaryEntity: GroupSummaryEntity): GroupSummary {
fun map(groupSummaryEntity: GroupSummaryEntity): GroupSummary {
return GroupSummary(
roomSummaryEntity.groupId,
roomSummaryEntity.displayName,
roomSummaryEntity.shortDescription,
roomSummaryEntity.avatarUrl,
roomSummaryEntity.roomIds.toList(),
roomSummaryEntity.userIds.toList()
groupSummaryEntity.groupId,
groupSummaryEntity.membership,
groupSummaryEntity.displayName,
groupSummaryEntity.shortDescription,
groupSummaryEntity.avatarUrl,
groupSummaryEntity.roomIds.toList(),
groupSummaryEntity.userIds.toList()
)
}
}

View file

@ -20,9 +20,13 @@ import im.vector.matrix.android.api.session.room.model.Membership
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
internal open class GroupEntity(@PrimaryKey var groupId: String = ""
) : RealmObject() {
/**
* This class is used to store group info (groupId and membership) from the sync response.
* Then [im.vector.matrix.android.internal.session.group.GroupSummaryUpdater] observes change and
* makes requests to fetch group information from the homeserver
*/
internal open class GroupEntity(@PrimaryKey var groupId: String = "")
: RealmObject() {
private var membershipStr: String = Membership.NONE.name
var membership: Membership

View file

@ -16,18 +16,28 @@
package im.vector.matrix.android.internal.database.model
import im.vector.matrix.android.api.session.room.model.Membership
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
internal open class GroupSummaryEntity(@PrimaryKey var groupId: String = "",
var displayName: String = "",
var shortDescription: String = "",
var avatarUrl: String = "",
var roomIds: RealmList<String> = RealmList(),
var userIds: RealmList<String> = RealmList()
var displayName: String = "",
var shortDescription: String = "",
var avatarUrl: String = "",
var roomIds: RealmList<String> = RealmList(),
var userIds: RealmList<String> = RealmList()
) : RealmObject() {
private var membershipStr: String = Membership.NONE.name
var membership: Membership
get() {
return Membership.valueOf(membershipStr)
}
set(value) {
membershipStr = value.name
}
companion object
}

View file

@ -23,9 +23,9 @@ import io.realm.Realm
import io.realm.RealmQuery
import io.realm.kotlin.where
internal fun GroupEntity.Companion.where(realm: Realm, roomId: String): RealmQuery<GroupEntity> {
internal fun GroupEntity.Companion.where(realm: Realm, groupId: String): RealmQuery<GroupEntity> {
return realm.where<GroupEntity>()
.equalTo(GroupEntityFields.GROUP_ID, roomId)
.equalTo(GroupEntityFields.GROUP_ID, groupId)
}
internal fun GroupEntity.Companion.where(realm: Realm, membership: Membership? = null): RealmQuery<GroupEntity> {

View file

@ -30,3 +30,7 @@ internal fun GroupSummaryEntity.Companion.where(realm: Realm, groupId: String? =
return query
}
internal fun GroupSummaryEntity.Companion.where(realm: Realm, groupIds: List<String>): RealmQuery<GroupSummaryEntity> {
return realm.where<GroupSummaryEntity>()
.`in`(GroupSummaryEntityFields.GROUP_ID, groupIds.toTypedArray())
}

View file

@ -17,6 +17,7 @@
package im.vector.matrix.android.internal.session.group
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.network.executeRequest
@ -64,8 +65,7 @@ internal class DefaultGetGroupDataTask @Inject constructor(
groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: ""
val name = groupSummary.profile?.name
groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupId else name
groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription
?: ""
groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription ?: ""
val roomIds = groupRooms.rooms.map { it.roomId }
groupSummaryEntity.roomIds.clear()
@ -74,8 +74,12 @@ internal class DefaultGetGroupDataTask @Inject constructor(
val userIds = groupUsers.users.map { it.userId }
groupSummaryEntity.userIds.clear()
groupSummaryEntity.userIds.addAll(userIds)
groupSummaryEntity.membership = when (groupSummary.user?.membership) {
Membership.JOIN.value -> Membership.JOIN
Membership.INVITE.value -> Membership.INVITE
else -> Membership.LEAVE
}
}
}
}

View file

@ -21,15 +21,15 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.model.GroupEntity
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.worker.WorkManagerUtil
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
import io.realm.OrderedCollectionChangeSet
import io.realm.RealmConfiguration
import io.realm.RealmResults
import javax.inject.Inject
@ -37,18 +37,30 @@ private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
internal class GroupSummaryUpdater @Inject constructor(private val context: Context,
private val credentials: Credentials,
@SessionDatabase realmConfiguration: RealmConfiguration)
: RealmLiveEntityObserver<GroupEntity>(realmConfiguration) {
private val monarchy: Monarchy)
: RealmLiveEntityObserver<GroupEntity>(monarchy.realmConfiguration) {
override val query = Monarchy.Query<GroupEntity> { GroupEntity.where(it) }
override val query = Monarchy.Query { GroupEntity.where(it) }
override fun onChange(results: RealmResults<GroupEntity>, changeSet: OrderedCollectionChangeSet) {
val newGroupIds = changeSet.insertions
// `insertions` for new groups and `changes` to handle left groups
val modifiedGroupEntity = (changeSet.insertions + changeSet.changes)
.asSequence()
.mapNotNull { results[it]?.groupId}
.toList()
.mapNotNull { results[it] }
val getGroupDataWorkerParams = GetGroupDataWorker.Params(credentials.userId, newGroupIds)
fetchGroupsData(modifiedGroupEntity
.filter { it.membership == Membership.JOIN || it.membership == Membership.INVITE }
.map { it.groupId }
.toList())
deleteGroups(modifiedGroupEntity
.filter { it.membership == Membership.LEAVE }
.map { it.groupId }
.toList())
}
private fun fetchGroupsData(groupIds: List<String>) {
val getGroupDataWorkerParams = GetGroupDataWorker.Params(credentials.userId, groupIds)
val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams)
val sendWork = matrixOneTimeWorkRequestBuilder<GetGroupDataWorker>()
@ -61,4 +73,15 @@ internal class GroupSummaryUpdater @Inject constructor(private val context: Cont
.enqueue()
}
/**
* Delete the GroupSummaryEntity of left groups
*/
private fun deleteGroups(groupIds: List<String>) {
monarchy
.writeAsync { realm ->
GroupSummaryEntity.where(realm, groupIds)
.findAll()
.deleteAllFromRealm()
}
}
}

View file

@ -64,12 +64,13 @@ internal class GroupSyncHandler @Inject constructor(private val monarchy: Monarc
}
}
/** Note: [im.vector.matrix.android.internal.session.group.GroupSummaryUpdater] is observing changes */
realm.insertOrUpdate(groups)
}
private fun handleJoinedGroup(realm: Realm,
groupId: String): GroupEntity {
val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId)
groupEntity.membership = Membership.JOIN
return groupEntity
@ -77,21 +78,16 @@ internal class GroupSyncHandler @Inject constructor(private val monarchy: Monarc
private fun handleInvitedGroup(realm: Realm,
groupId: String): GroupEntity {
val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId)
groupEntity.membership = Membership.INVITE
return groupEntity
}
// TODO : handle it
private fun handleLeftGroup(realm: Realm,
groupId: String): GroupEntity {
val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId)
groupEntity.membership = Membership.LEAVE
return groupEntity
}
}

View file

@ -21,11 +21,19 @@ import android.content.Intent
import android.os.Build
import im.vector.matrix.android.internal.session.sync.job.SyncService
import im.vector.riotx.R
import im.vector.riotx.core.extensions.vectorComponent
import im.vector.riotx.features.notifications.NotificationUtils
import timber.log.Timber
class VectorSyncService : SyncService() {
private lateinit var notificationUtils: NotificationUtils
override fun onCreate() {
super.onCreate()
notificationUtils = vectorComponent().notificationUtils()
}
override fun onDestroy() {
removeForegroundNotif()
super.onDestroy()
@ -43,7 +51,7 @@ class VectorSyncService : SyncService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Timber.v("VectorSyncService - onStartCommand ")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notification = NotificationUtils.buildForegroundServiceNotification(applicationContext, R.string.notification_listening_for_events, false)
val notification = notificationUtils.buildForegroundServiceNotification(R.string.notification_listening_for_events, false)
startForeground(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE, notification)
}
return super.onStartCommand(intent, flags, startId)

View file

@ -26,6 +26,7 @@ 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.model.Membership
import im.vector.matrix.rx.rx
import im.vector.riotx.R
import im.vector.riotx.core.extensions.postLiveEvent
@ -93,20 +94,20 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
session
.rx()
.liveGroupSummaries()
// Keep only joined groups. Group invitations will be managed later
.map { it.filter { groupSummary -> groupSummary.membership == Membership.JOIN } }
.map {
val myUser = session.getUser(session.myUserId)
val allCommunityGroup = GroupSummary(
groupId = ALL_COMMUNITIES_GROUP_ID,
membership = Membership.JOIN,
displayName = stringProvider.getString(R.string.group_all_communities),
avatarUrl = myUser?.avatarUrl ?: "")
listOf(allCommunityGroup) + it
}
.execute { async ->
// TODO Phase2 Handle the case where the selected group is deleted on another client
val newSelectedGroup = selectedGroup ?: async()?.firstOrNull()
copy(asyncGroups = async, selectedGroup = newSelectedGroup)
}
}
}