Merge branch 'develop' into dla/feature/custom_room_notification_settings_list
This commit is contained in:
commit
e2adecbcf4
30 changed files with 306 additions and 39 deletions
|
|
@ -238,7 +238,7 @@ class MessagesFlowNode @AssistedInject constructor(
|
|||
backstack.push(navTarget)
|
||||
}
|
||||
is TimelineItemAudioContent -> {
|
||||
val mediaSource = event.content.audioSource
|
||||
val mediaSource = event.content.mediaSource
|
||||
val navTarget = NavTarget.MediaViewer(
|
||||
mediaInfo = MediaInfo(
|
||||
name = event.content.body,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package io.element.android.features.messages.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.lifecycle.subscribe
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
|
|
@ -28,6 +29,8 @@ import dagger.assisted.AssistedInject
|
|||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.messages.impl.attachments.Attachment
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
|
||||
import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
|
@ -44,6 +47,7 @@ class MessagesNode @AssistedInject constructor(
|
|||
private val room: MatrixRoom,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val presenterFactory: MessagesPresenter.Factory,
|
||||
private val timelineItemPresenterFactories: TimelineItemPresenterFactories,
|
||||
) : Node(buildContext, plugins = plugins), MessagesNavigator {
|
||||
|
||||
private val presenter = presenterFactory.create(this)
|
||||
|
|
@ -106,17 +110,21 @@ class MessagesNode @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
MessagesView(
|
||||
state = state,
|
||||
onBackPressed = this::navigateUp,
|
||||
onRoomDetailsClicked = this::onRoomDetailsClicked,
|
||||
onEventClicked = this::onEventClicked,
|
||||
onPreviewAttachments = this::onPreviewAttachments,
|
||||
onUserDataClicked = this::onUserDataClicked,
|
||||
onSendLocationClicked = this::onSendLocationClicked,
|
||||
onCreatePollClicked = this::onCreatePollClicked,
|
||||
modifier = modifier,
|
||||
)
|
||||
CompositionLocalProvider(
|
||||
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,
|
||||
) {
|
||||
val state = presenter.present()
|
||||
MessagesView(
|
||||
state = state,
|
||||
onBackPressed = this::navigateUp,
|
||||
onRoomDetailsClicked = this::onRoomDetailsClicked,
|
||||
onEventClicked = this::onEventClicked,
|
||||
onPreviewAttachments = this::onPreviewAttachments,
|
||||
onUserDataClicked = this::onUserDataClicked,
|
||||
onSendLocationClicked = this::onSendLocationClicked,
|
||||
onCreatePollClicked = this::onCreatePollClicked,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -222,6 +223,14 @@ fun MessagesView(
|
|||
ReinviteDialog(
|
||||
state = state
|
||||
)
|
||||
|
||||
// Since the textfield is now based on an Android view, this is no longer done automatically.
|
||||
// We need to hide the keyboard automatically when navigating out of this screen.
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
localView.hideKeyboard()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.di
|
||||
|
||||
import dagger.MapKey
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Annotation to add a factory of type [TimelineItemPresenterFactory] to a
|
||||
* Dagger map multi binding keyed with a subclass of [TimelineItemEventContent].
|
||||
*/
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MapKey
|
||||
annotation class TimelineItemEventContentKey(val value: KClass<out TimelineItemEventContent>)
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.di
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Module
|
||||
import dagger.multibindings.Multibinds
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Dagger module that declares the [TimelineItemPresenterFactory] map multi binding.
|
||||
*
|
||||
* Its sole purpose is to support the case of an empty map multibinding.
|
||||
*/
|
||||
@Module
|
||||
@ContributesTo(RoomScope::class)
|
||||
interface TimelineItemPresenterFactoriesModule {
|
||||
@Multibinds
|
||||
fun multiBindTimelineItemPresenterFactories(): @JvmSuppressWildcards Map<Class<out TimelineItemEventContent>, TimelineItemPresenterFactory<*, *>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around the [TimelineItemPresenterFactory] map multi binding.
|
||||
*
|
||||
* Its only purpose is to provide a nicer type name than:
|
||||
* `@JvmSuppressWildcards Map<Class<out TimelineItemEventContent>, TimelineItemPresenterFactory<*, *>>`.
|
||||
*
|
||||
* A typealias would have been better but typealiases on Dagger types which use @JvmSuppressWildcards
|
||||
* currently make Dagger crash.
|
||||
*
|
||||
* Request this type from Dagger to access the [TimelineItemPresenterFactory] map multibinding.
|
||||
*/
|
||||
data class TimelineItemPresenterFactories @Inject constructor(
|
||||
val factories: @JvmSuppressWildcards Map<Class<out TimelineItemEventContent>, TimelineItemPresenterFactory<*, *>>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Provides a [TimelineItemPresenterFactories] to the composition.
|
||||
*/
|
||||
val LocalTimelineItemPresenterFactories = staticCompositionLocalOf {
|
||||
TimelineItemPresenterFactories(emptyMap())
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and remembers a presenter for the given content.
|
||||
*
|
||||
* Will throw if the presenter is not found in the [TimelineItemPresenterFactory] map multi binding.
|
||||
*/
|
||||
@Composable
|
||||
inline fun <reified C : TimelineItemEventContent, reified S : Any> TimelineItemPresenterFactories.rememberPresenter(
|
||||
content: C
|
||||
): Presenter<S> = remember(content) {
|
||||
factories.getValue(C::class.java).let {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(it as TimelineItemPresenterFactory<C, S>).create(content)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.di
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
|
||||
/**
|
||||
* A factory for a [Presenter] associated with a timeline item.
|
||||
*
|
||||
* Implementations should be annotated with [AssistedFactory] to be created by Dagger.
|
||||
*
|
||||
* @param C The timeline item's [TimelineItemEventContent] subtype.
|
||||
* @param S The [Presenter]'s state class.
|
||||
* @return A [Presenter] that produces a state of type [S] for the given content of type [C].
|
||||
*/
|
||||
fun interface TimelineItemPresenterFactory<C : TimelineItemEventContent, S : Any> {
|
||||
fun create(content: C): Presenter<S>
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessage
|
|||
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
||||
import java.time.Duration
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineItemContentMessageFactory @Inject constructor(
|
||||
|
|
@ -103,11 +104,11 @@ class TimelineItemContentMessageFactory @Inject constructor(
|
|||
}
|
||||
is AudioMessageType -> TimelineItemAudioContent(
|
||||
body = messageType.body,
|
||||
audioSource = messageType.source,
|
||||
duration = messageType.info?.duration?.toMillis() ?: 0L,
|
||||
mediaSource = messageType.source,
|
||||
duration = messageType.info?.duration ?: Duration.ZERO,
|
||||
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
|
||||
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
|
||||
fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
|
||||
fileExtension = fileExtensionExtractor.extractFromName(messageType.body),
|
||||
)
|
||||
is FileMessageType -> {
|
||||
val fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
|
||||
|
|
|
|||
|
|
@ -18,11 +18,12 @@ package io.element.android.features.messages.impl.timeline.model.event
|
|||
|
||||
import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import java.time.Duration
|
||||
|
||||
data class TimelineItemAudioContent(
|
||||
val body: String,
|
||||
val duration: Long,
|
||||
val audioSource: MediaSource,
|
||||
val duration: Duration,
|
||||
val mediaSource: MediaSource,
|
||||
val mimeType: String,
|
||||
val formattedFileSize: String,
|
||||
val fileExtension: String,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline.model.event
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import java.time.Duration
|
||||
|
||||
open class TimelineItemAudioContentProvider : PreviewParameterProvider<TimelineItemAudioContent> {
|
||||
override val values: Sequence<TimelineItemAudioContent>
|
||||
|
|
@ -34,6 +35,6 @@ fun aTimelineItemAudioContent(fileName: String = "A sound.mp3") = TimelineItemAu
|
|||
mimeType = MimeTypes.Pdf,
|
||||
formattedFileSize = "100kB",
|
||||
fileExtension = "mp3",
|
||||
duration = 100,
|
||||
audioSource = MediaSource(""),
|
||||
duration = Duration.ofMillis(100),
|
||||
mediaSource = MediaSource(""),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue