Timeline : exposes same methods as the rust type and use them by default on liveTimeline
This commit is contained in:
parent
6af4bf30ff
commit
b40f01a634
3 changed files with 457 additions and 188 deletions
|
|
@ -17,8 +17,20 @@
|
||||||
package io.element.android.libraries.matrix.api.timeline
|
package io.element.android.libraries.matrix.api.timeline
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||||
|
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||||
|
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||||
|
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||||
|
import io.element.android.libraries.matrix.api.room.Mention
|
||||||
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
interface Timeline : AutoCloseable {
|
interface Timeline : AutoCloseable {
|
||||||
|
|
||||||
|
|
@ -39,4 +51,119 @@ interface Timeline : AutoCloseable {
|
||||||
suspend fun paginate(direction: PaginationDirection): Result<Boolean>
|
suspend fun paginate(direction: PaginationDirection): Result<Boolean>
|
||||||
fun paginationStatus(direction: PaginationDirection): StateFlow<PaginationStatus>
|
fun paginationStatus(direction: PaginationDirection): StateFlow<PaginationStatus>
|
||||||
val timelineItems: Flow<List<MatrixTimelineItem>>
|
val timelineItems: Flow<List<MatrixTimelineItem>>
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun enterSpecialMode(eventId: EventId?): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun sendImage(
|
||||||
|
file: File,
|
||||||
|
thumbnailFile: File?,
|
||||||
|
imageInfo: ImageInfo,
|
||||||
|
body: String?,
|
||||||
|
formattedBody: String?,
|
||||||
|
progressCallback: ProgressCallback?
|
||||||
|
): Result<MediaUploadHandler>
|
||||||
|
|
||||||
|
suspend fun sendVideo(
|
||||||
|
file: File,
|
||||||
|
thumbnailFile: File?,
|
||||||
|
videoInfo: VideoInfo,
|
||||||
|
body: String?,
|
||||||
|
formattedBody: String?,
|
||||||
|
progressCallback: ProgressCallback?
|
||||||
|
): Result<MediaUploadHandler>
|
||||||
|
|
||||||
|
suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler>
|
||||||
|
|
||||||
|
suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler>
|
||||||
|
|
||||||
|
suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun retrySendMessage(transactionId: TransactionId): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun cancelSend(transactionId: TransactionId): Result<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share a location message in the room.
|
||||||
|
*
|
||||||
|
* @param body A human readable textual representation of the location.
|
||||||
|
* @param geoUri A geo URI (RFC 5870) representing the location e.g. `geo:51.5008,0.1247;u=35`.
|
||||||
|
* Respectively: latitude, longitude, and (optional) uncertainty.
|
||||||
|
* @param description Optional description of the location to display to the user.
|
||||||
|
* @param zoomLevel Optional zoom level to display the map at.
|
||||||
|
* @param assetType Optional type of the location asset.
|
||||||
|
* Set to SENDER if sharing own location. Set to PIN if sharing any location.
|
||||||
|
*/
|
||||||
|
suspend fun sendLocation(
|
||||||
|
body: String,
|
||||||
|
geoUri: String,
|
||||||
|
description: String? = null,
|
||||||
|
zoomLevel: Int? = null,
|
||||||
|
assetType: AssetType? = null,
|
||||||
|
): Result<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a poll in the room.
|
||||||
|
*
|
||||||
|
* @param question The question to ask.
|
||||||
|
* @param answers The list of answers.
|
||||||
|
* @param maxSelections The maximum number of answers that can be selected.
|
||||||
|
* @param pollKind The kind of poll to create.
|
||||||
|
*/
|
||||||
|
suspend fun createPoll(
|
||||||
|
question: String,
|
||||||
|
answers: List<String>,
|
||||||
|
maxSelections: Int,
|
||||||
|
pollKind: PollKind,
|
||||||
|
): Result<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit a poll in the room.
|
||||||
|
*
|
||||||
|
* @param pollStartId The event ID of the poll start event.
|
||||||
|
* @param question The question to ask.
|
||||||
|
* @param answers The list of answers.
|
||||||
|
* @param maxSelections The maximum number of answers that can be selected.
|
||||||
|
* @param pollKind The kind of poll to create.
|
||||||
|
*/
|
||||||
|
suspend fun editPoll(
|
||||||
|
pollStartId: EventId,
|
||||||
|
question: String,
|
||||||
|
answers: List<String>,
|
||||||
|
maxSelections: Int,
|
||||||
|
pollKind: PollKind,
|
||||||
|
): Result<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a response to a poll.
|
||||||
|
*
|
||||||
|
* @param pollStartId The event ID of the poll start event.
|
||||||
|
* @param answers The list of answer ids to send.
|
||||||
|
*/
|
||||||
|
suspend fun sendPollResponse(pollStartId: EventId, answers: List<String>): Result<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends a poll in the room.
|
||||||
|
*
|
||||||
|
* @param pollStartId The event ID of the poll start event.
|
||||||
|
* @param text Fallback text of the poll end event.
|
||||||
|
*/
|
||||||
|
suspend fun endPoll(pollStartId: EventId, text: String): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun sendVoiceMessage(
|
||||||
|
file: File,
|
||||||
|
audioInfo: AudioInfo,
|
||||||
|
waveform: List<Float>,
|
||||||
|
progressCallback: ProgressCallback?
|
||||||
|
): Result<MediaUploadHandler>
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,17 +43,12 @@ import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels
|
import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||||
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
|
|
||||||
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
|
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
|
||||||
import io.element.android.libraries.matrix.impl.media.map
|
|
||||||
import io.element.android.libraries.matrix.impl.media.toMSC3246range
|
|
||||||
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.impl.poll.toInner
|
|
||||||
import io.element.android.libraries.matrix.impl.room.location.toInner
|
|
||||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
|
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
|
||||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
||||||
import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper
|
import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper
|
||||||
|
|
@ -80,8 +75,6 @@ import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
|
||||||
import org.matrix.rustcomponents.sdk.MessageFormat
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||||
|
|
@ -94,10 +87,8 @@ import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider
|
||||||
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
|
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
|
||||||
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
|
||||||
import uniffi.matrix_sdk.RoomPowerLevelChanges
|
import uniffi.matrix_sdk.RoomPowerLevelChanges
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import org.matrix.rustcomponents.sdk.FormattedBody as RustFormattedBody
|
|
||||||
import org.matrix.rustcomponents.sdk.Room as InnerRoom
|
import org.matrix.rustcomponents.sdk.Room as InnerRoom
|
||||||
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||||
|
|
||||||
|
|
@ -198,7 +189,6 @@ class RustMatrixRoom(
|
||||||
liveTimeline.close()
|
liveTimeline.close()
|
||||||
innerRoom.destroy()
|
innerRoom.destroy()
|
||||||
roomListItem.destroy()
|
roomListItem.destroy()
|
||||||
specialModeEventTimelineItem?.destroy()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val name: String?
|
override val name: String?
|
||||||
|
|
@ -332,12 +322,8 @@ class RustMatrixRoom(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> {
|
||||||
messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()).use { content ->
|
return liveTimeline.sendMessage(body, htmlBody, mentions)
|
||||||
runCatching {
|
|
||||||
innerTimeline.send(content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun editMessage(
|
override suspend fun editMessage(
|
||||||
|
|
@ -346,45 +332,16 @@ class RustMatrixRoom(
|
||||||
body: String,
|
body: String,
|
||||||
htmlBody: String?,
|
htmlBody: String?,
|
||||||
mentions: List<Mention>,
|
mentions: List<Mention>,
|
||||||
): Result<Unit> =
|
): Result<Unit> {
|
||||||
withContext(roomDispatcher) {
|
return liveTimeline.editMessage(originalEventId, transactionId, body, htmlBody, mentions)
|
||||||
if (originalEventId != null) {
|
|
||||||
runCatching {
|
|
||||||
val editedEvent = specialModeEventTimelineItem ?: innerTimeline.getEventTimelineItemByEventId(originalEventId.value)
|
|
||||||
editedEvent.use {
|
|
||||||
innerTimeline.edit(
|
|
||||||
newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()),
|
|
||||||
editItem = it,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
specialModeEventTimelineItem = null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
runCatching {
|
|
||||||
transactionId?.let { cancelSend(it) }
|
|
||||||
innerTimeline.send(messageEventContentFromParts(body, htmlBody))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var specialModeEventTimelineItem: EventTimelineItem? = null
|
|
||||||
|
|
||||||
override suspend fun enterSpecialMode(eventId: EventId?): Result<Unit> = withContext(roomDispatcher) {
|
|
||||||
runCatching {
|
|
||||||
specialModeEventTimelineItem?.destroy()
|
|
||||||
specialModeEventTimelineItem = null
|
|
||||||
specialModeEventTimelineItem = eventId?.let { innerTimeline.getEventTimelineItemByEventId(it.value) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun enterSpecialMode(eventId: EventId?): Result<Unit> {
|
||||||
runCatching {
|
return liveTimeline.enterSpecialMode(eventId)
|
||||||
val inReplyTo = specialModeEventTimelineItem ?: innerTimeline.getEventTimelineItemByEventId(eventId.value)
|
}
|
||||||
inReplyTo.use { eventTimelineItem ->
|
|
||||||
innerTimeline.sendReply(messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), eventTimelineItem)
|
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>{
|
||||||
}
|
return liveTimeline.replyMessage(eventId, body, htmlBody, mentions)
|
||||||
specialModeEventTimelineItem = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun redactEvent(eventId: EventId, reason: String?) = withContext(roomDispatcher) {
|
override suspend fun redactEvent(eventId: EventId, reason: String?) = withContext(roomDispatcher) {
|
||||||
|
|
@ -467,18 +424,7 @@ class RustMatrixRoom(
|
||||||
formattedBody: String?,
|
formattedBody: String?,
|
||||||
progressCallback: ProgressCallback?,
|
progressCallback: ProgressCallback?,
|
||||||
): Result<MediaUploadHandler> {
|
): Result<MediaUploadHandler> {
|
||||||
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
return liveTimeline.sendImage(file, thumbnailFile, imageInfo, body, formattedBody, progressCallback)
|
||||||
innerTimeline.sendImage(
|
|
||||||
url = file.path,
|
|
||||||
thumbnailUrl = thumbnailFile?.path,
|
|
||||||
imageInfo = imageInfo.map(),
|
|
||||||
caption = body,
|
|
||||||
formattedCaption = formattedBody?.let {
|
|
||||||
RustFormattedBody(body = it, format = MessageFormat.Html)
|
|
||||||
},
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendVideo(
|
override suspend fun sendVideo(
|
||||||
|
|
@ -489,63 +435,31 @@ class RustMatrixRoom(
|
||||||
formattedBody: String?,
|
formattedBody: String?,
|
||||||
progressCallback: ProgressCallback?,
|
progressCallback: ProgressCallback?,
|
||||||
): Result<MediaUploadHandler> {
|
): Result<MediaUploadHandler> {
|
||||||
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
return liveTimeline.sendVideo(file, thumbnailFile, videoInfo, body, formattedBody, progressCallback)
|
||||||
innerTimeline.sendVideo(
|
|
||||||
url = file.path,
|
|
||||||
thumbnailUrl = thumbnailFile?.path,
|
|
||||||
videoInfo = videoInfo.map(),
|
|
||||||
caption = body,
|
|
||||||
formattedCaption = formattedBody?.let {
|
|
||||||
RustFormattedBody(body = it, format = MessageFormat.Html)
|
|
||||||
},
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
|
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
|
||||||
return sendAttachment(listOf(file)) {
|
return liveTimeline.sendAudio(file, audioInfo, progressCallback)
|
||||||
innerTimeline.sendAudio(
|
|
||||||
url = file.path,
|
|
||||||
audioInfo = audioInfo.map(),
|
|
||||||
// Maybe allow a caption in the future?
|
|
||||||
caption = null,
|
|
||||||
formattedCaption = null,
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
|
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
|
||||||
return sendAttachment(listOf(file)) {
|
return liveTimeline.sendFile(file, fileInfo, progressCallback)
|
||||||
innerTimeline.sendFile(file.path, fileInfo.map(), progressCallback?.toProgressWatcher())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit>{
|
||||||
runCatching {
|
return liveTimeline.toggleReaction(emoji, eventId)
|
||||||
innerTimeline.toggleReaction(key = emoji, eventId = eventId.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit>{
|
||||||
runCatching {
|
return liveTimeline.forwardEvent(eventId, roomIds)
|
||||||
roomContentForwarder.forward(fromTimeline = innerTimeline, eventId = eventId, toRoomIds = roomIds)
|
|
||||||
}.onFailure {
|
|
||||||
Timber.e(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun retrySendMessage(transactionId: TransactionId): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun retrySendMessage(transactionId: TransactionId): Result<Unit> {
|
||||||
runCatching {
|
return liveTimeline.retrySendMessage(transactionId)
|
||||||
innerTimeline.retrySend(transactionId.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit>{
|
||||||
runCatching {
|
return liveTimeline.cancelSend(transactionId)
|
||||||
innerTimeline.cancelSend(transactionId.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit> = withContext(roomDispatcher) {
|
||||||
|
|
@ -623,16 +537,8 @@ class RustMatrixRoom(
|
||||||
description: String?,
|
description: String?,
|
||||||
zoomLevel: Int?,
|
zoomLevel: Int?,
|
||||||
assetType: AssetType?,
|
assetType: AssetType?,
|
||||||
): Result<Unit> = withContext(roomDispatcher) {
|
): Result<Unit> {
|
||||||
runCatching {
|
return liveTimeline.sendLocation(body, geoUri, description, zoomLevel, assetType)
|
||||||
innerTimeline.sendLocation(
|
|
||||||
body = body,
|
|
||||||
geoUri = geoUri,
|
|
||||||
description = description,
|
|
||||||
zoomLevel = zoomLevel?.toUByte(),
|
|
||||||
assetType = assetType?.toInner(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun createPoll(
|
override suspend fun createPoll(
|
||||||
|
|
@ -640,15 +546,8 @@ class RustMatrixRoom(
|
||||||
answers: List<String>,
|
answers: List<String>,
|
||||||
maxSelections: Int,
|
maxSelections: Int,
|
||||||
pollKind: PollKind,
|
pollKind: PollKind,
|
||||||
): Result<Unit> = withContext(roomDispatcher) {
|
): Result<Unit> {
|
||||||
runCatching {
|
return liveTimeline.createPoll(question, answers, maxSelections, pollKind)
|
||||||
innerTimeline.createPoll(
|
|
||||||
question = question,
|
|
||||||
answers = answers,
|
|
||||||
maxSelections = maxSelections.toUByte(),
|
|
||||||
pollKind = pollKind.toInner(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun editPoll(
|
override suspend fun editPoll(
|
||||||
|
|
@ -657,46 +556,22 @@ class RustMatrixRoom(
|
||||||
answers: List<String>,
|
answers: List<String>,
|
||||||
maxSelections: Int,
|
maxSelections: Int,
|
||||||
pollKind: PollKind,
|
pollKind: PollKind,
|
||||||
): Result<Unit> = withContext(roomDispatcher) {
|
): Result<Unit> {
|
||||||
runCatching {
|
return liveTimeline.editPoll(pollStartId, question, answers, maxSelections, pollKind)
|
||||||
val pollStartEvent =
|
|
||||||
innerTimeline.getEventTimelineItemByEventId(
|
|
||||||
eventId = pollStartId.value
|
|
||||||
)
|
|
||||||
pollStartEvent.use {
|
|
||||||
innerTimeline.editPoll(
|
|
||||||
question = question,
|
|
||||||
answers = answers,
|
|
||||||
maxSelections = maxSelections.toUByte(),
|
|
||||||
pollKind = pollKind.toInner(),
|
|
||||||
editItem = pollStartEvent,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendPollResponse(
|
override suspend fun sendPollResponse(
|
||||||
pollStartId: EventId,
|
pollStartId: EventId,
|
||||||
answers: List<String>
|
answers: List<String>
|
||||||
): Result<Unit> = withContext(roomDispatcher) {
|
): Result<Unit> {
|
||||||
runCatching {
|
return liveTimeline.sendPollResponse(pollStartId, answers)
|
||||||
innerTimeline.sendPollResponse(
|
|
||||||
pollStartId = pollStartId.value,
|
|
||||||
answers = answers,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun endPoll(
|
override suspend fun endPoll(
|
||||||
pollStartId: EventId,
|
pollStartId: EventId,
|
||||||
text: String
|
text: String
|
||||||
): Result<Unit> = withContext(roomDispatcher) {
|
): Result<Unit> {
|
||||||
runCatching {
|
return liveTimeline.endPoll(pollStartId, text)
|
||||||
innerTimeline.endPoll(
|
|
||||||
pollStartId = pollStartId.value,
|
|
||||||
text = text,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendVoiceMessage(
|
override suspend fun sendVoiceMessage(
|
||||||
|
|
@ -704,16 +579,8 @@ class RustMatrixRoom(
|
||||||
audioInfo: AudioInfo,
|
audioInfo: AudioInfo,
|
||||||
waveform: List<Float>,
|
waveform: List<Float>,
|
||||||
progressCallback: ProgressCallback?,
|
progressCallback: ProgressCallback?,
|
||||||
): Result<MediaUploadHandler> = sendAttachment(listOf(file)) {
|
): Result<MediaUploadHandler>{
|
||||||
innerTimeline.sendVoiceMessage(
|
return liveTimeline.sendVoiceMessage(file, audioInfo, waveform, progressCallback)
|
||||||
url = file.path,
|
|
||||||
audioInfo = audioInfo.map(),
|
|
||||||
waveform = waveform.toMSC3246range(),
|
|
||||||
// Maybe allow a caption in the future?
|
|
||||||
caption = null,
|
|
||||||
formattedCaption = null,
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun typingNotice(isTyping: Boolean) = runCatching {
|
override suspend fun typingNotice(isTyping: Boolean) = runCatching {
|
||||||
|
|
@ -749,12 +616,6 @@ class RustMatrixRoom(
|
||||||
innerRoom.matrixToEventPermalink(eventId.value)
|
innerRoom.matrixToEventPermalink(eventId.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> {
|
|
||||||
return runCatching {
|
|
||||||
MediaUploadHandlerImpl(files, handle())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createTimeline(
|
private fun createTimeline(
|
||||||
timeline: InnerTimeline,
|
timeline: InnerTimeline,
|
||||||
isLive: Boolean = true,
|
isLive: Boolean = true,
|
||||||
|
|
@ -769,19 +630,9 @@ class RustMatrixRoom(
|
||||||
dispatcher = roomDispatcher,
|
dispatcher = roomDispatcher,
|
||||||
lastLoginTimestamp = sessionData.loginTimestamp,
|
lastLoginTimestamp = sessionData.loginTimestamp,
|
||||||
onNewSyncedEvent = onNewSyncedEvent,
|
onNewSyncedEvent = onNewSyncedEvent,
|
||||||
|
roomContentForwarder = roomContentForwarder,
|
||||||
inner = timeline,
|
inner = timeline,
|
||||||
fetchDetailsForEvent = { eventId ->
|
|
||||||
runCatching {
|
|
||||||
innerTimeline.getEventTimelineItemByEventId(eventId.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun messageEventContentFromParts(body: String, htmlBody: String?): RoomMessageEventContentWithoutRelation =
|
|
||||||
if (htmlBody != null) {
|
|
||||||
messageEventContentFromHtml(body, htmlBody)
|
|
||||||
} else {
|
|
||||||
messageEventContentFromMarkdown(body)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,30 @@
|
||||||
package io.element.android.libraries.matrix.impl.timeline
|
package io.element.android.libraries.matrix.impl.timeline
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||||
|
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||||
|
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||||
|
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.api.room.Mention
|
||||||
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
import io.element.android.libraries.matrix.api.timeline.TimelineException
|
import io.element.android.libraries.matrix.api.timeline.TimelineException
|
||||||
|
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
|
||||||
|
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
|
||||||
|
import io.element.android.libraries.matrix.impl.media.map
|
||||||
|
import io.element.android.libraries.matrix.impl.media.toMSC3246range
|
||||||
|
import io.element.android.libraries.matrix.impl.poll.toInner
|
||||||
|
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||||
|
import io.element.android.libraries.matrix.impl.room.location.toInner
|
||||||
|
import io.element.android.libraries.matrix.impl.room.map
|
||||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper
|
import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper
|
||||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||||
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
||||||
|
|
@ -48,10 +67,19 @@ import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||||
|
import org.matrix.rustcomponents.sdk.FormattedBody
|
||||||
|
import org.matrix.rustcomponents.sdk.MessageFormat
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
||||||
|
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||||
import org.matrix.rustcomponents.sdk.TimelineDiff
|
import org.matrix.rustcomponents.sdk.TimelineDiff
|
||||||
import org.matrix.rustcomponents.sdk.TimelineItem
|
import org.matrix.rustcomponents.sdk.TimelineItem
|
||||||
|
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
|
||||||
|
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
||||||
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uniffi.matrix_sdk_ui.EventItemOrigin
|
import uniffi.matrix_sdk_ui.EventItemOrigin
|
||||||
|
import java.io.File
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||||
|
|
@ -68,7 +96,7 @@ class RustTimeline(
|
||||||
private val matrixRoom: MatrixRoom,
|
private val matrixRoom: MatrixRoom,
|
||||||
private val dispatcher: CoroutineDispatcher,
|
private val dispatcher: CoroutineDispatcher,
|
||||||
private val lastLoginTimestamp: Date?,
|
private val lastLoginTimestamp: Date?,
|
||||||
private val fetchDetailsForEvent: suspend (EventId) -> Result<Unit>,
|
private val roomContentForwarder: RoomContentForwarder,
|
||||||
private val onNewSyncedEvent: () -> Unit,
|
private val onNewSyncedEvent: () -> Unit,
|
||||||
) : Timeline {
|
) : Timeline {
|
||||||
|
|
||||||
|
|
@ -90,7 +118,7 @@ class RustTimeline(
|
||||||
private val invisibleIndicatorPostProcessor = InvisibleIndicatorPostProcessor(isLive)
|
private val invisibleIndicatorPostProcessor = InvisibleIndicatorPostProcessor(isLive)
|
||||||
|
|
||||||
private val timelineItemFactory = MatrixTimelineItemMapper(
|
private val timelineItemFactory = MatrixTimelineItemMapper(
|
||||||
fetchDetailsForEvent = fetchDetailsForEvent,
|
fetchDetailsForEvent = this::fetchDetailsForEvent,
|
||||||
roomCoroutineScope = roomCoroutineScope,
|
roomCoroutineScope = roomCoroutineScope,
|
||||||
virtualTimelineItemMapper = VirtualTimelineItemMapper(),
|
virtualTimelineItemMapper = VirtualTimelineItemMapper(),
|
||||||
eventTimelineItemMapper = EventTimelineItemMapper(
|
eventTimelineItemMapper = EventTimelineItemMapper(
|
||||||
|
|
@ -138,7 +166,7 @@ class RustTimeline(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePaginationStatus(direction: Timeline.PaginationDirection, update: (Timeline.PaginationStatus)->Timeline.PaginationStatus){
|
private fun updatePaginationStatus(direction: Timeline.PaginationDirection, update: (Timeline.PaginationStatus) -> Timeline.PaginationStatus) {
|
||||||
when (direction) {
|
when (direction) {
|
||||||
Timeline.PaginationDirection.BACKWARDS -> backPaginationStatus.getAndUpdate(update)
|
Timeline.PaginationDirection.BACKWARDS -> backPaginationStatus.getAndUpdate(update)
|
||||||
Timeline.PaginationDirection.FORWARDS -> forwardPaginationStatus.getAndUpdate(update)
|
Timeline.PaginationDirection.FORWARDS -> forwardPaginationStatus.getAndUpdate(update)
|
||||||
|
|
@ -204,6 +232,7 @@ class RustTimeline(
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
inner.close()
|
inner.close()
|
||||||
|
specialModeEventTimelineItem?.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchMembers() = withContext(dispatcher) {
|
private suspend fun fetchMembers() = withContext(dispatcher) {
|
||||||
|
|
@ -229,4 +258,266 @@ class RustTimeline(
|
||||||
initLatch.await()
|
initLatch.await()
|
||||||
timelineDiffProcessor.postDiffs(diffs)
|
timelineDiffProcessor.postDiffs(diffs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(dispatcher) {
|
||||||
|
messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()).use { content ->
|
||||||
|
runCatching {
|
||||||
|
inner.send(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun editMessage(
|
||||||
|
originalEventId: EventId?,
|
||||||
|
transactionId: TransactionId?,
|
||||||
|
body: String,
|
||||||
|
htmlBody: String?,
|
||||||
|
mentions: List<Mention>,
|
||||||
|
): Result<Unit> =
|
||||||
|
withContext(dispatcher) {
|
||||||
|
if (originalEventId != null) {
|
||||||
|
runCatching {
|
||||||
|
val editedEvent = specialModeEventTimelineItem ?: inner.getEventTimelineItemByEventId(originalEventId.value)
|
||||||
|
editedEvent.use {
|
||||||
|
inner.edit(
|
||||||
|
newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()),
|
||||||
|
editItem = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
specialModeEventTimelineItem = null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runCatching {
|
||||||
|
transactionId?.let { cancelSend(it) }
|
||||||
|
inner.send(messageEventContentFromParts(body, htmlBody))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var specialModeEventTimelineItem: EventTimelineItem? = null
|
||||||
|
|
||||||
|
override suspend fun enterSpecialMode(eventId: EventId?): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
specialModeEventTimelineItem?.destroy()
|
||||||
|
specialModeEventTimelineItem = null
|
||||||
|
specialModeEventTimelineItem = eventId?.let { inner.getEventTimelineItemByEventId(it.value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
val inReplyTo = specialModeEventTimelineItem ?: inner.getEventTimelineItemByEventId(eventId.value)
|
||||||
|
inReplyTo.use { eventTimelineItem ->
|
||||||
|
inner.sendReply(messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), eventTimelineItem)
|
||||||
|
}
|
||||||
|
specialModeEventTimelineItem = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sendImage(
|
||||||
|
file: File,
|
||||||
|
thumbnailFile: File?,
|
||||||
|
imageInfo: ImageInfo,
|
||||||
|
body: String?,
|
||||||
|
formattedBody: String?,
|
||||||
|
progressCallback: ProgressCallback?,
|
||||||
|
): Result<MediaUploadHandler> {
|
||||||
|
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
||||||
|
inner.sendImage(
|
||||||
|
url = file.path,
|
||||||
|
thumbnailUrl = thumbnailFile?.path,
|
||||||
|
imageInfo = imageInfo.map(),
|
||||||
|
caption = body,
|
||||||
|
formattedCaption = formattedBody?.let {
|
||||||
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
|
},
|
||||||
|
progressWatcher = progressCallback?.toProgressWatcher()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sendVideo(
|
||||||
|
file: File,
|
||||||
|
thumbnailFile: File?,
|
||||||
|
videoInfo: VideoInfo,
|
||||||
|
body: String?,
|
||||||
|
formattedBody: String?,
|
||||||
|
progressCallback: ProgressCallback?,
|
||||||
|
): Result<MediaUploadHandler> {
|
||||||
|
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
||||||
|
inner.sendVideo(
|
||||||
|
url = file.path,
|
||||||
|
thumbnailUrl = thumbnailFile?.path,
|
||||||
|
videoInfo = videoInfo.map(),
|
||||||
|
caption = body,
|
||||||
|
formattedCaption = formattedBody?.let {
|
||||||
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
|
},
|
||||||
|
progressWatcher = progressCallback?.toProgressWatcher()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
|
||||||
|
return sendAttachment(listOf(file)) {
|
||||||
|
inner.sendAudio(
|
||||||
|
url = file.path,
|
||||||
|
audioInfo = audioInfo.map(),
|
||||||
|
// Maybe allow a caption in the future?
|
||||||
|
caption = null,
|
||||||
|
formattedCaption = null,
|
||||||
|
progressWatcher = progressCallback?.toProgressWatcher()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
|
||||||
|
return sendAttachment(listOf(file)) {
|
||||||
|
inner.sendFile(file.path, fileInfo.map(), progressCallback?.toProgressWatcher())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
inner.toggleReaction(key = emoji, eventId = eventId.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
roomContentForwarder.forward(fromTimeline = inner, eventId = eventId, toRoomIds = roomIds)
|
||||||
|
}.onFailure {
|
||||||
|
Timber.e(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun retrySendMessage(transactionId: TransactionId): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
inner.retrySend(transactionId.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
inner.cancelSend(transactionId.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sendLocation(
|
||||||
|
body: String,
|
||||||
|
geoUri: String,
|
||||||
|
description: String?,
|
||||||
|
zoomLevel: Int?,
|
||||||
|
assetType: AssetType?,
|
||||||
|
): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
inner.sendLocation(
|
||||||
|
body = body,
|
||||||
|
geoUri = geoUri,
|
||||||
|
description = description,
|
||||||
|
zoomLevel = zoomLevel?.toUByte(),
|
||||||
|
assetType = assetType?.toInner(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun createPoll(
|
||||||
|
question: String,
|
||||||
|
answers: List<String>,
|
||||||
|
maxSelections: Int,
|
||||||
|
pollKind: PollKind,
|
||||||
|
): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
inner.createPoll(
|
||||||
|
question = question,
|
||||||
|
answers = answers,
|
||||||
|
maxSelections = maxSelections.toUByte(),
|
||||||
|
pollKind = pollKind.toInner(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun editPoll(
|
||||||
|
pollStartId: EventId,
|
||||||
|
question: String,
|
||||||
|
answers: List<String>,
|
||||||
|
maxSelections: Int,
|
||||||
|
pollKind: PollKind,
|
||||||
|
): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
val pollStartEvent =
|
||||||
|
inner.getEventTimelineItemByEventId(
|
||||||
|
eventId = pollStartId.value
|
||||||
|
)
|
||||||
|
pollStartEvent.use {
|
||||||
|
inner.editPoll(
|
||||||
|
question = question,
|
||||||
|
answers = answers,
|
||||||
|
maxSelections = maxSelections.toUByte(),
|
||||||
|
pollKind = pollKind.toInner(),
|
||||||
|
editItem = pollStartEvent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sendPollResponse(
|
||||||
|
pollStartId: EventId,
|
||||||
|
answers: List<String>
|
||||||
|
): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
inner.sendPollResponse(
|
||||||
|
pollStartId = pollStartId.value,
|
||||||
|
answers = answers,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun endPoll(
|
||||||
|
pollStartId: EventId,
|
||||||
|
text: String
|
||||||
|
): Result<Unit> = withContext(dispatcher) {
|
||||||
|
runCatching {
|
||||||
|
inner.endPoll(
|
||||||
|
pollStartId = pollStartId.value,
|
||||||
|
text = text,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sendVoiceMessage(
|
||||||
|
file: File,
|
||||||
|
audioInfo: AudioInfo,
|
||||||
|
waveform: List<Float>,
|
||||||
|
progressCallback: ProgressCallback?,
|
||||||
|
): Result<MediaUploadHandler> = sendAttachment(listOf(file)) {
|
||||||
|
inner.sendVoiceMessage(
|
||||||
|
url = file.path,
|
||||||
|
audioInfo = audioInfo.map(),
|
||||||
|
waveform = waveform.toMSC3246range(),
|
||||||
|
// Maybe allow a caption in the future?
|
||||||
|
caption = null,
|
||||||
|
formattedCaption = null,
|
||||||
|
progressWatcher = progressCallback?.toProgressWatcher(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun messageEventContentFromParts(body: String, htmlBody: String?): RoomMessageEventContentWithoutRelation =
|
||||||
|
if (htmlBody != null) {
|
||||||
|
messageEventContentFromHtml(body, htmlBody)
|
||||||
|
} else {
|
||||||
|
messageEventContentFromMarkdown(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> {
|
||||||
|
return runCatching {
|
||||||
|
MediaUploadHandlerImpl(files, handle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchDetailsForEvent(eventId: EventId): Result<Unit> {
|
||||||
|
return runCatching {
|
||||||
|
inner.getEventTimelineItemByEventId(eventId.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue