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
8675bf5215
commit
285066c206
119 changed files with 1520 additions and 339 deletions
|
|
@ -16,6 +16,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.mediaviewer.api)
|
||||
implementation(projects.libraries.preferences.api)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.api.timeline.voicemessages.composer
|
||||
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent
|
||||
import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent
|
||||
|
||||
sealed interface VoiceMessageComposerEvents {
|
||||
data class RecorderEvent(
|
||||
val recorderEvent: VoiceMessageRecorderEvent
|
||||
) : VoiceMessageComposerEvents
|
||||
data class PlayerEvent(
|
||||
val playerEvent: VoiceMessagePlayerEvent,
|
||||
) : VoiceMessageComposerEvents
|
||||
data object SendVoiceMessage : VoiceMessageComposerEvents
|
||||
data object DeleteVoiceMessage : VoiceMessageComposerEvents
|
||||
data object AcceptPermissionRationale : VoiceMessageComposerEvents
|
||||
data object DismissPermissionsRationale : VoiceMessageComposerEvents
|
||||
data class LifecycleEvent(val event: Lifecycle.Event) : VoiceMessageComposerEvents
|
||||
data object DismissSendFailureDialog : VoiceMessageComposerEvents
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.api.timeline.voicemessages.composer
|
||||
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
|
||||
fun interface VoiceMessageComposerPresenter : Presenter<VoiceMessageComposerState> {
|
||||
interface Factory {
|
||||
fun create(timelineMode: Timeline.Mode): VoiceMessageComposerPresenter
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.api.timeline.voicemessages.composer
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import io.element.android.libraries.textcomposer.model.VoiceMessageState
|
||||
|
||||
@Stable
|
||||
data class VoiceMessageComposerState(
|
||||
val voiceMessageState: VoiceMessageState,
|
||||
val showPermissionRationaleDialog: Boolean,
|
||||
val showSendFailureDialog: Boolean,
|
||||
val keepScreenOn: Boolean,
|
||||
val eventSink: (VoiceMessageComposerEvents) -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.api.timeline.voicemessages.composer
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.designsystem.components.media.createFakeWaveform
|
||||
import io.element.android.libraries.textcomposer.model.VoiceMessageState
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
open class VoiceMessageComposerStateProvider : PreviewParameterProvider<VoiceMessageComposerState> {
|
||||
override val values: Sequence<VoiceMessageComposerState>
|
||||
get() = sequenceOf(
|
||||
aVoiceMessageComposerState(voiceMessageState = VoiceMessageState.Recording(duration = 61.seconds, levels = aWaveformLevels)),
|
||||
)
|
||||
}
|
||||
|
||||
fun aVoiceMessageComposerState(
|
||||
voiceMessageState: VoiceMessageState = VoiceMessageState.Idle,
|
||||
keepScreenOn: Boolean = false,
|
||||
showPermissionRationaleDialog: Boolean = false,
|
||||
showSendFailureDialog: Boolean = false,
|
||||
) = VoiceMessageComposerState(
|
||||
voiceMessageState = voiceMessageState,
|
||||
showPermissionRationaleDialog = showPermissionRationaleDialog,
|
||||
showSendFailureDialog = showSendFailureDialog,
|
||||
keepScreenOn = keepScreenOn,
|
||||
eventSink = {},
|
||||
)
|
||||
|
||||
fun aVoiceMessagePreviewState() = VoiceMessageState.Preview(
|
||||
isSending = false,
|
||||
isPlaying = false,
|
||||
showCursor = false,
|
||||
playbackProgress = 0f,
|
||||
time = 10.seconds,
|
||||
waveform = createFakeWaveform(),
|
||||
)
|
||||
|
||||
internal var aWaveformLevels = List(100) { it.toFloat() / 100 }.toPersistentList()
|
||||
Loading…
Add table
Add a link
Reference in a new issue