Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send and render typing events (#564) #833

Merged
merged 3 commits into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Changes in RiotX 0.13.0 (2020-XX-XX)
===================================================

Features ✨:
-
- Send and render typing events (#564)

Improvements 🙌:
-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.relation.RelationService
import im.vector.matrix.android.api.session.room.notification.RoomPushRuleService
import im.vector.matrix.android.api.session.room.reporting.ReportingService
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.api.session.room.reporting.ReportingService
import im.vector.matrix.android.api.session.room.send.DraftService
import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.session.room.state.StateService
import im.vector.matrix.android.api.session.room.timeline.TimelineService
import im.vector.matrix.android.api.session.room.typing.TypingService
import im.vector.matrix.android.api.util.Optional

/**
Expand All @@ -38,6 +39,7 @@ interface Room :
SendService,
DraftService,
ReadService,
TypingService,
MembershipService,
StateService,
ReportingService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ data class RoomSummary(
val versioningState: VersioningState = VersioningState.NONE,
val readMarkerId: String? = null,
val userDrafts: List<UserDraft> = emptyList(),
var isEncrypted: Boolean
var isEncrypted: Boolean,
val typingRoomMemberIds: List<String> = emptyList()
) {

val isVersioned: Boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.typing

/**
* This interface defines methods to handle typing data. It's implemented at the room level.
*/
interface TypingService {

/**
* To call when user is typing a message in the room
* The SDK will handle the requests scheduling to the homeserver:
* - No more than one typing request per 10s
* - If not called after 10s, the SDK will notify the homeserver that the user is not typing anymore
*/
fun userIsTyping()

/**
* To call when user stops typing in the room
* Notify immediately the homeserver that the user is not typing anymore in the room, for
* instance when user has emptied the composer, or when the user quits the timeline screen.
*/
fun userStopsTyping()
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import java.util.UUID
import java.util.*
import javax.inject.Inject

internal class RoomSummaryMapper @Inject constructor(
Expand Down Expand Up @@ -71,7 +71,8 @@ internal class RoomSummaryMapper @Inject constructor(
userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) } ?: emptyList(),
canonicalAlias = roomSummaryEntity.canonicalAlias,
aliases = roomSummaryEntity.aliases.toList(),
isEncrypted = roomSummaryEntity.isEncrypted
isEncrypted = roomSummaryEntity.isEncrypted,
typingRoomMemberIds = roomSummaryEntity.typingUserIds.toList()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
var aliases: RealmList<String> = RealmList(),
// this is required for querying
var flatAliases: String = "",
var isEncrypted: Boolean = false
var isEncrypted: Boolean = false,
var typingUserIds: RealmList<String> = RealmList()
) : RealmObject() {

private var membershipStr: String = Membership.NONE.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import im.vector.matrix.android.api.session.room.send.DraftService
import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.session.room.state.StateService
import im.vector.matrix.android.api.session.room.timeline.TimelineService
import im.vector.matrix.android.api.session.room.typing.TypingService
import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.api.util.toOptional
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
Expand All @@ -49,6 +50,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
private val stateService: StateService,
private val reportingService: ReportingService,
private val readService: ReadService,
private val typingService: TypingService,
private val cryptoService: CryptoService,
private val relationService: RelationService,
private val roomMembersService: MembershipService,
Expand All @@ -60,6 +62,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
StateService by stateService,
ReportingService by reportingService,
ReadService by readService,
TypingService by typingService,
RelationService by relationService,
MembershipService by roomMembersService,
RoomPushRuleService by roomPushRuleService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,21 @@ package im.vector.matrix.android.internal.session.room

import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
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.thirdparty.ThirdPartyProtocol
import im.vector.matrix.android.internal.network.NetworkConstants
import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription
import im.vector.matrix.android.internal.session.room.membership.RoomMembersResponse
import im.vector.matrix.android.internal.session.room.membership.joining.InviteBody
import im.vector.matrix.android.internal.session.room.relation.RelationsResponse
import im.vector.matrix.android.internal.session.room.reporting.ReportContentBody
import im.vector.matrix.android.internal.session.room.send.SendResponse
import im.vector.matrix.android.internal.session.room.timeline.EventContextResponse
import im.vector.matrix.android.internal.session.room.timeline.PaginationResponse
import im.vector.matrix.android.internal.session.room.typing.TypingBody
import retrofit2.Call
import retrofit2.http.*

Expand Down Expand Up @@ -268,4 +269,12 @@ internal interface RoomAPI {
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call<RoomAliasDescription>

/**
* Inform that the user is starting to type or has stopped typing
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/typing/{userId}")
fun sendTypingState(@Path("roomId") roomId: String,
@Path("userId") userId: String,
@Body body: TypingBody): Call<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import im.vector.matrix.android.internal.session.room.reporting.DefaultReporting
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
import im.vector.matrix.android.internal.session.room.state.DefaultStateService
import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService
import im.vector.matrix.android.internal.session.room.typing.DefaultTypingService
import javax.inject.Inject

internal interface RoomFactory {
Expand All @@ -46,6 +47,7 @@ internal class DefaultRoomFactory @Inject constructor(private val monarchy: Mona
private val stateServiceFactory: DefaultStateService.Factory,
private val reportingServiceFactory: DefaultReportingService.Factory,
private val readServiceFactory: DefaultReadService.Factory,
private val typingServiceFactory: DefaultTypingService.Factory,
private val relationServiceFactory: DefaultRelationService.Factory,
private val membershipServiceFactory: DefaultMembershipService.Factory,
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory) :
Expand All @@ -62,6 +64,7 @@ internal class DefaultRoomFactory @Inject constructor(private val monarchy: Mona
stateServiceFactory.create(roomId),
reportingServiceFactory.create(roomId),
readServiceFactory.create(roomId),
typingServiceFactory.create(roomId),
cryptoService,
relationServiceFactory.create(roomId),
membershipServiceFactory.create(roomId),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import im.vector.matrix.android.internal.session.room.reporting.ReportContentTas
import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask
import im.vector.matrix.android.internal.session.room.state.SendStateTask
import im.vector.matrix.android.internal.session.room.timeline.*
import im.vector.matrix.android.internal.session.room.typing.DefaultSendTypingTask
import im.vector.matrix.android.internal.session.room.typing.SendTypingTask
import retrofit2.Retrofit

@Module
Expand All @@ -68,74 +70,77 @@ internal abstract class RoomModule {
}

@Binds
abstract fun bindRoomFactory(roomFactory: DefaultRoomFactory): RoomFactory
abstract fun bindRoomFactory(factory: DefaultRoomFactory): RoomFactory

@Binds
abstract fun bindRoomService(roomService: DefaultRoomService): RoomService
abstract fun bindRoomService(service: DefaultRoomService): RoomService

@Binds
abstract fun bindRoomDirectoryService(roomDirectoryService: DefaultRoomDirectoryService): RoomDirectoryService
abstract fun bindRoomDirectoryService(service: DefaultRoomDirectoryService): RoomDirectoryService

@Binds
abstract fun bindEventRelationsAggregationTask(eventRelationsAggregationTask: DefaultEventRelationsAggregationTask): EventRelationsAggregationTask
abstract fun bindFileService(service: DefaultFileService): FileService

@Binds
abstract fun bindCreateRoomTask(createRoomTask: DefaultCreateRoomTask): CreateRoomTask
abstract fun bindEventRelationsAggregationTask(task: DefaultEventRelationsAggregationTask): EventRelationsAggregationTask

@Binds
abstract fun bindGetPublicRoomTask(getPublicRoomTask: DefaultGetPublicRoomTask): GetPublicRoomTask
abstract fun bindCreateRoomTask(task: DefaultCreateRoomTask): CreateRoomTask

@Binds
abstract fun bindGetThirdPartyProtocolsTask(getThirdPartyProtocolsTask: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask
abstract fun bindGetPublicRoomTask(task: DefaultGetPublicRoomTask): GetPublicRoomTask

@Binds
abstract fun bindInviteTask(inviteTask: DefaultInviteTask): InviteTask
abstract fun bindGetThirdPartyProtocolsTask(task: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask

@Binds
abstract fun bindJoinRoomTask(joinRoomTask: DefaultJoinRoomTask): JoinRoomTask
abstract fun bindInviteTask(task: DefaultInviteTask): InviteTask

@Binds
abstract fun bindLeaveRoomTask(leaveRoomTask: DefaultLeaveRoomTask): LeaveRoomTask
abstract fun bindJoinRoomTask(task: DefaultJoinRoomTask): JoinRoomTask

@Binds
abstract fun bindLoadRoomMembersTask(loadRoomMembersTask: DefaultLoadRoomMembersTask): LoadRoomMembersTask
abstract fun bindLeaveRoomTask(task: DefaultLeaveRoomTask): LeaveRoomTask

@Binds
abstract fun bindPruneEventTask(pruneEventTask: DefaultPruneEventTask): PruneEventTask
abstract fun bindLoadRoomMembersTask(task: DefaultLoadRoomMembersTask): LoadRoomMembersTask

@Binds
abstract fun bindSetReadMarkersTask(setReadMarkersTask: DefaultSetReadMarkersTask): SetReadMarkersTask
abstract fun bindPruneEventTask(task: DefaultPruneEventTask): PruneEventTask

@Binds
abstract fun bindMarkAllRoomsReadTask(markAllRoomsReadTask: DefaultMarkAllRoomsReadTask): MarkAllRoomsReadTask
abstract fun bindSetReadMarkersTask(task: DefaultSetReadMarkersTask): SetReadMarkersTask

@Binds
abstract fun bindFindReactionEventForUndoTask(findReactionEventForUndoTask: DefaultFindReactionEventForUndoTask): FindReactionEventForUndoTask
abstract fun bindMarkAllRoomsReadTask(task: DefaultMarkAllRoomsReadTask): MarkAllRoomsReadTask

@Binds
abstract fun bindUpdateQuickReactionTask(updateQuickReactionTask: DefaultUpdateQuickReactionTask): UpdateQuickReactionTask
abstract fun bindFindReactionEventForUndoTask(task: DefaultFindReactionEventForUndoTask): FindReactionEventForUndoTask

@Binds
abstract fun bindSendStateTask(sendStateTask: DefaultSendStateTask): SendStateTask
abstract fun bindUpdateQuickReactionTask(task: DefaultUpdateQuickReactionTask): UpdateQuickReactionTask

@Binds
abstract fun bindReportContentTask(reportContentTask: DefaultReportContentTask): ReportContentTask
abstract fun bindSendStateTask(task: DefaultSendStateTask): SendStateTask

@Binds
abstract fun bindGetContextOfEventTask(getContextOfEventTask: DefaultGetContextOfEventTask): GetContextOfEventTask
abstract fun bindReportContentTask(task: DefaultReportContentTask): ReportContentTask

@Binds
abstract fun bindClearUnlinkedEventsTask(clearUnlinkedEventsTask: DefaultClearUnlinkedEventsTask): ClearUnlinkedEventsTask
abstract fun bindGetContextOfEventTask(task: DefaultGetContextOfEventTask): GetContextOfEventTask

@Binds
abstract fun bindPaginationTask(paginationTask: DefaultPaginationTask): PaginationTask
abstract fun bindClearUnlinkedEventsTask(task: DefaultClearUnlinkedEventsTask): ClearUnlinkedEventsTask

@Binds
abstract fun bindFileService(fileService: DefaultFileService): FileService
abstract fun bindPaginationTask(task: DefaultPaginationTask): PaginationTask

@Binds
abstract fun bindFetchEditHistoryTask(fetchEditHistoryTask: DefaultFetchEditHistoryTask): FetchEditHistoryTask
abstract fun bindFetchEditHistoryTask(task: DefaultFetchEditHistoryTask): FetchEditHistoryTask

@Binds
abstract fun bindGetRoomIdByAliasTask(getRoomIdByAliasTask: DefaultGetRoomIdByAliasTask): GetRoomIdByAliasTask
abstract fun bindGetRoomIdByAliasTask(task: DefaultGetRoomIdByAliasTask): GetRoomIdByAliasTask

@Binds
abstract fun bindSendTypingTask(task: DefaultSendTypingTask): SendTypingTask
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.model.*
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.query.*
import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import im.vector.matrix.android.internal.session.sync.RoomSyncHandler
import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary
import im.vector.matrix.android.internal.session.sync.model.RoomSyncUnreadNotifications
import io.realm.Realm
Expand Down Expand Up @@ -65,7 +66,8 @@ internal class RoomSummaryUpdater @Inject constructor(
membership: Membership? = null,
roomSummary: RoomSyncSummary? = null,
unreadNotifications: RoomSyncUnreadNotifications? = null,
updateMembers: Boolean = false) {
updateMembers: Boolean = false,
ephemeralResult: RoomSyncHandler.EphemeralResult? = null) {
val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
if (roomSummary != null) {
if (roomSummary.heroes.isNotEmpty()) {
Expand Down Expand Up @@ -93,8 +95,8 @@ internal class RoomSummaryUpdater @Inject constructor(
val encryptionEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ENCRYPTION).prev()

roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
// avoid this call if we are sure there are unread events
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
// avoid this call if we are sure there are unread events
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)

roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
Expand All @@ -104,11 +106,13 @@ internal class RoomSummaryUpdater @Inject constructor(
?.canonicalAlias

val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel<RoomAliasesContent>()?.aliases
?: emptyList()
?: emptyList()
roomSummaryEntity.aliases.clear()
roomSummaryEntity.aliases.addAll(roomAliases)
roomSummaryEntity.flatAliases = roomAliases.joinToString(separator = "|", prefix = "|")
roomSummaryEntity.isEncrypted = encryptionEvent != null
roomSummaryEntity.typingUserIds.clear()
roomSummaryEntity.typingUserIds.addAll(ephemeralResult?.typingUserIds.orEmpty())

if (updateMembers) {
val otherRoomMembers = RoomMembers(realm, roomId)
Expand Down
Loading