Threads - first iteration (#5165)
* Initial threads support: parse `ThreadSummary`. Replace several `isThreaded` values with `EventThreadInfo`, which contains the info about the event either being the root of a thread or part of it. * Add `Threaded` timeline mode * Add a `liveTimeline` parameter to `TimelineController`'s constructor. This way we can customise which timeline will be used as the 'live' one. Also add `@LiveTimeline` DI qualifier for the actual live timeline of the room. * Create `ThreadedMessagesNode`. Allow opening a thread in a separate screen. * Add the callbacks for the list menu actions - even if they're the wrong ones and will send the data to the room instead * Send attachments and location in threads * Fix polls in threads, add support for sending voice messages in threads * Display thread summaries only when the feature flag is enabled * Use 'Reply' instead of 'Reply in thread' when in threaded timeline mode * Remove incorrect usage of `Timeline` in `MessageComposerPresenter`. This led to replies to threaded events not appearing as actual replies. --------- Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
parent
cc10ba41fd
commit
35928e3630
119 changed files with 1520 additions and 339 deletions
|
|
@ -8,21 +8,33 @@
|
|||
package io.element.android.libraries.mediaupload.api
|
||||
|
||||
import android.net.Uri
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.libraries.core.extensions.flatMap
|
||||
import io.element.android.libraries.core.extensions.flatMapCatching
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Job
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
class MediaSender @Inject constructor(
|
||||
class MediaSender @AssistedInject constructor(
|
||||
private val preProcessor: MediaPreProcessor,
|
||||
private val room: JoinedRoom,
|
||||
@Assisted private val timelineMode: Timeline.Mode,
|
||||
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
|
||||
) {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(
|
||||
timelineMode: Timeline.Mode,
|
||||
): MediaSender
|
||||
}
|
||||
|
||||
private val ongoingUploadJobs = ConcurrentHashMap<Job.Key, MediaUploadHandler>()
|
||||
val hasOngoingMediaUploads get() = ongoingUploadJobs.isNotEmpty()
|
||||
|
||||
|
|
@ -46,12 +58,14 @@ class MediaSender @Inject constructor(
|
|||
formattedCaption: String?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<Unit> {
|
||||
return room.liveTimeline.sendMedia(
|
||||
uploadInfo = mediaUploadInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
return getTimeline().flatMap {
|
||||
it.sendMedia(
|
||||
uploadInfo = mediaUploadInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
}
|
||||
.handleSendResult()
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +85,7 @@ class MediaSender @Inject constructor(
|
|||
mediaOptimizationConfig = mediaOptimizationConfig,
|
||||
)
|
||||
.flatMapCatching { info ->
|
||||
room.liveTimeline.sendMedia(
|
||||
getTimeline().getOrThrow().sendMedia(
|
||||
uploadInfo = info,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
|
|
@ -101,7 +115,7 @@ class MediaSender @Inject constructor(
|
|||
audioInfo = audioInfo,
|
||||
waveform = waveForm,
|
||||
)
|
||||
room.liveTimeline.sendMedia(
|
||||
getTimeline().getOrThrow().sendMedia(
|
||||
uploadInfo = newInfo,
|
||||
caption = null,
|
||||
formattedCaption = null,
|
||||
|
|
@ -186,6 +200,15 @@ class MediaSender @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getTimeline(): Result<Timeline> {
|
||||
return when (timelineMode) {
|
||||
is Timeline.Mode.Thread -> {
|
||||
room.createTimeline(CreateTimelineParams.Threaded(threadRootEventId = timelineMode.threadRootId))
|
||||
}
|
||||
else -> Result.success(room.liveTimeline)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up any temporary files or resources used during the media processing.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.api.core.EventId
|
|||
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.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
|
|
@ -160,6 +161,7 @@ class MediaSenderTest {
|
|||
) = MediaSender(
|
||||
preProcessor = preProcessor,
|
||||
room = room,
|
||||
timelineMode = Timeline.Mode.Live,
|
||||
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue