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

Rework on media module #3967

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import io.element.android.libraries.architecture.BackstackWithOverlayBox
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.overlay.Overlay
import io.element.android.libraries.architecture.overlay.operation.hide
import io.element.android.libraries.architecture.overlay.operation.show
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.MatrixClient
Expand All @@ -66,8 +67,8 @@ import io.element.android.libraries.matrix.api.room.joinedRoomMembers
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.ui.messages.LocalRoomMemberProfilesCache
import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
import io.element.android.libraries.mediaviewer.api.MediaInfo
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.libraries.textcomposer.mentions.LocalMentionSpanTheme
import io.element.android.libraries.textcomposer.mentions.MentionSpanTheme
import io.element.android.services.analytics.api.AnalyticsService
Expand All @@ -86,6 +87,7 @@ class MessagesFlowNode @AssistedInject constructor(
private val showLocationEntryPoint: ShowLocationEntryPoint,
private val createPollEntryPoint: CreatePollEntryPoint,
private val elementCallEntryPoint: ElementCallEntryPoint,
private val mediaViewerEntryPoint: MediaViewerEntryPoint,
private val analyticsService: AnalyticsService,
private val room: MatrixRoom,
private val roomMemberProfilesCache: RoomMemberProfilesCache,
Expand Down Expand Up @@ -228,14 +230,22 @@ class MessagesFlowNode @AssistedInject constructor(
createNode<MessagesNode>(buildContext, listOf(callback, inputs))
}
is NavTarget.MediaViewer -> {
val inputs = MediaViewerNode.Inputs(
val params = MediaViewerEntryPoint.Params(
mediaInfo = navTarget.mediaInfo,
mediaSource = navTarget.mediaSource,
thumbnailSource = navTarget.thumbnailSource,
canDownload = true,
canShare = true,
)
createNode<MediaViewerNode>(buildContext, listOf(inputs))
val callback = object : MediaViewerEntryPoint.Callback {
override fun onDone() {
overlay.hide()
}
}
mediaViewerEntryPoint.nodeBuilder(this, buildContext)
.params(params)
.callback(callback)
.build()
}
is NavTarget.AttachmentPreview -> {
val inputs = AttachmentsPreviewNode.Inputs(navTarget.attachment)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer

@ContributesNode(RoomScope::class)
class AttachmentsPreviewNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
presenterFactory: AttachmentsPreviewPresenter.Factory,
private val localMediaRenderer: LocalMediaRenderer,
) : Node(buildContext, plugins = plugins) {
data class Inputs(val attachment: Attachment) : NodeInputs

Expand All @@ -46,6 +48,7 @@ class AttachmentsPreviewNode @AssistedInject constructor(
val state = presenter.present()
AttachmentsPreviewView(
state = state,
localMediaRenderer = localMediaRenderer,
modifier = modifier
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,16 @@ package io.element.android.features.messages.impl.attachments.preview
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.core.net.toUri
import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.libraries.mediaviewer.api.MediaInfo
import io.element.android.libraries.mediaviewer.api.anImageMediaInfo
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
import io.element.android.libraries.mediaviewer.api.local.aVideoMediaInfo
import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo
import io.element.android.libraries.mediaviewer.api.local.anAudioMediaInfo
import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo
import io.element.android.libraries.textcomposer.model.TextEditorState
import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown

open class AttachmentsPreviewStateProvider : PreviewParameterProvider<AttachmentsPreviewState> {
override val values: Sequence<AttachmentsPreviewState>
get() = sequenceOf(
anAttachmentsPreviewState(),
anAttachmentsPreviewState(mediaInfo = aVideoMediaInfo()),
anAttachmentsPreviewState(mediaInfo = anAudioMediaInfo()),
anAttachmentsPreviewState(mediaInfo = anApkMediaInfo()),
Copy link
Member Author

@bmarty bmarty Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the content is not the real content anymore, there is no need to have multiple identical previews.

anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Processing),
anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f)),
anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package io.element.android.features.messages.impl.attachments.preview

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.IntrinsicSize
Expand All @@ -20,6 +21,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
Expand All @@ -34,20 +36,20 @@ import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.mediaviewer.api.local.LocalMediaView
import io.element.android.libraries.mediaviewer.api.local.rememberLocalMediaViewState
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer
import io.element.android.libraries.textcomposer.TextComposer
import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.libraries.textcomposer.model.VoiceMessageState
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.wysiwyg.display.TextDisplay
import me.saket.telephoto.zoomable.ZoomSpec
import me.saket.telephoto.zoomable.rememberZoomableState

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AttachmentsPreviewView(
state: AttachmentsPreviewState,
localMediaRenderer: LocalMediaRenderer,
modifier: Modifier = Modifier,
) {
fun postSendAttachment() {
Expand Down Expand Up @@ -82,6 +84,7 @@ fun AttachmentsPreviewView(
) {
AttachmentPreviewContent(
state = state,
localMediaRenderer = localMediaRenderer,
onSendClick = ::postSendAttachment,
)
}
Expand Down Expand Up @@ -129,6 +132,7 @@ private fun AttachmentSendStateView(
@Composable
private fun AttachmentPreviewContent(
state: AttachmentsPreviewState,
localMediaRenderer: LocalMediaRenderer,
onSendClick: () -> Unit,
) {
Box(
Expand All @@ -142,17 +146,7 @@ private fun AttachmentPreviewContent(
) {
when (val attachment = state.attachment) {
is Attachment.Media -> {
val localMediaViewState = rememberLocalMediaViewState(
zoomableState = rememberZoomableState(
zoomSpec = ZoomSpec(maxZoomFactor = 4f, preventOverOrUnderZoom = false)
)
)
LocalMediaView(
modifier = Modifier.fillMaxSize(),
localMedia = attachment.localMedia,
localMediaViewState = localMediaViewState,
onClick = {}
)
localMediaRenderer.Render(attachment.localMedia)
}
}
}
Expand Down Expand Up @@ -205,5 +199,15 @@ private fun AttachmentsPreviewBottomActions(
internal fun AttachmentsPreviewViewPreview(@PreviewParameter(AttachmentsPreviewStateProvider::class) state: AttachmentsPreviewState) = ElementPreviewDark {
AttachmentsPreviewView(
state = state,
localMediaRenderer = object : LocalMediaRenderer {
@Composable
override fun Render(localMedia: LocalMedia) {
Image(
painter = painterResource(id = CommonDrawables.sample_background),
modifier = Modifier.fillMaxSize(),
contentDescription = null,
)
}
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation
import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.TestScope

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import io.element.android.libraries.matrix.test.media.aMediaSource
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
import io.element.android.libraries.matrix.test.timeline.aStickerContent
import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation
import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.test.runTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
Expand All @@ -32,20 +33,16 @@ import io.element.android.features.roomdetails.impl.members.details.RoomMemberDe
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode
import io.element.android.features.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode
import io.element.android.features.userprofile.shared.UserProfileNodeHelper
import io.element.android.features.userprofile.shared.avatar.AvatarPreviewNode
import io.element.android.libraries.architecture.BackstackWithOverlayBox
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.overlay.operation.show
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.parcelize.Parcelize
Expand All @@ -59,6 +56,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
private val room: MatrixRoom,
private val analyticsService: AnalyticsService,
private val messagesEntryPoint: MessagesEntryPoint,
private val mediaViewerEntryPoint: MediaViewerEntryPoint,
) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>(
backstack = BackStack(
initialElement = plugins.filterIsInstance<RoomDetailsEntryPoint.Params>().first().initialElement.toNavTarget(),
Expand Down Expand Up @@ -202,22 +200,18 @@ class RoomDetailsFlowNode @AssistedInject constructor(
createNode<RoomMemberDetailsNode>(buildContext, plugins)
}
is NavTarget.AvatarPreview -> {
// We need to fake the MimeType here for the viewer to work.
val mimeType = MimeTypes.Images
val input = MediaViewerNode.Inputs(
mediaInfo = MediaInfo(
filename = navTarget.name,
caption = null,
mimeType = mimeType,
formattedFileSize = "",
fileExtension = ""
),
mediaSource = MediaSource(url = navTarget.avatarUrl),
thumbnailSource = null,
canDownload = false,
canShare = false,
)
createNode<AvatarPreviewNode>(buildContext, listOf(input))
val callback = object : MediaViewerEntryPoint.Callback {
override fun onDone() {
backstack.pop()
}
}
mediaViewerEntryPoint.nodeBuilder(this, buildContext)
.avatar(
navTarget.name,
navTarget.avatarUrl,
)
.callback(callback)
.build()
}

is NavTarget.PollHistory -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
Expand All @@ -24,18 +25,14 @@ import io.element.android.features.call.api.ElementCallEntryPoint
import io.element.android.features.userprofile.api.UserProfileEntryPoint
import io.element.android.features.userprofile.impl.root.UserProfileNode
import io.element.android.features.userprofile.shared.UserProfileNodeHelper
import io.element.android.features.userprofile.shared.avatar.AvatarPreviewNode
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import kotlinx.parcelize.Parcelize

@ContributesNode(SessionScope::class)
Expand All @@ -44,6 +41,7 @@ class UserProfileFlowNode @AssistedInject constructor(
@Assisted plugins: List<Plugin>,
private val elementCallEntryPoint: ElementCallEntryPoint,
private val sessionIdHolder: CurrentSessionIdHolder,
private val mediaViewerEntryPoint: MediaViewerEntryPoint,
) : BaseFlowNode<UserProfileFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.Root,
Expand Down Expand Up @@ -80,22 +78,18 @@ class UserProfileFlowNode @AssistedInject constructor(
createNode<UserProfileNode>(buildContext, listOf(callback, params))
}
is NavTarget.AvatarPreview -> {
// We need to fake the MimeType here for the viewer to work.
val mimeType = MimeTypes.Images
val input = MediaViewerNode.Inputs(
mediaInfo = MediaInfo(
val callback = object : MediaViewerEntryPoint.Callback {
override fun onDone() {
backstack.pop()
}
}
mediaViewerEntryPoint.nodeBuilder(this, buildContext)
.avatar(
filename = navTarget.name,
caption = null,
mimeType = mimeType,
formattedFileSize = "",
fileExtension = "",
),
mediaSource = MediaSource(url = navTarget.avatarUrl),
thumbnailSource = null,
canDownload = false,
canShare = false,
)
createNode<AvatarPreviewNode>(buildContext, listOf(input))
avatarUrl = navTarget.avatarUrl
)
.callback(callback)
.build()
}
}
}
Expand Down

This file was deleted.

Loading