Browse Source

Merge pull request #3694 from element-hq/feature/bma/oidcPrompt

OIDC prompt
pull/3698/merge
Benoit Marty 1 day ago committed by GitHub
parent
commit
1510141add
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
  2. 68
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt
  3. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
  4. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt
  5. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt
  6. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt
  7. 14
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt
  8. 10
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt
  9. 12
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt
  10. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt
  11. 4
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt
  12. 4
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt
  13. 2
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt
  14. 2
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt
  15. 7
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt
  16. 4
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt
  17. 2
      gradle/libs.versions.toml
  18. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt
  19. 51
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt
  20. 4
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt
  21. 3
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt
  22. 13
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt
  23. 22
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt
  24. 8
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
  25. 4
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt
  26. 2
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt
  27. 21
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt
  28. 11
      libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt
  29. 18
      libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt
  30. 11
      libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustLazyTimelineItemProvider.kt
  31. 3
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt
  32. 2
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt
  33. 4
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt
  34. 2
      libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt
  35. 2
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt
  36. 8
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt
  37. 2
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt

4
features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt

@ -27,6 +27,7 @@ import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.OidcPrompt
import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oidc.api.OidcAction
import io.element.android.libraries.oidc.api.OidcActionFlow import io.element.android.libraries.oidc.api.OidcActionFlow
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -92,7 +93,8 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!! val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!!
if (matrixHomeServerDetails.supportsOidcLogin) { if (matrixHomeServerDetails.supportsOidcLogin) {
// Retrieve the details right now // Retrieve the details right now
LoginFlow.OidcFlow(authenticationService.getOidcUrl().getOrThrow()) val oidcPrompt = if (params.isAccountCreation) OidcPrompt.Create else OidcPrompt.Consent
LoginFlow.OidcFlow(authenticationService.getOidcUrl(oidcPrompt).getOrThrow())
} else if (params.isAccountCreation) { } else if (params.isAccountCreation) {
val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl) val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl)
LoginFlow.AccountCreationFlow(url) LoginFlow.AccountCreationFlow(url)

68
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt

@ -31,103 +31,109 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
return sequenceOf( return sequenceOf(
anActionListState(), anActionListState(),
anActionListState().copy(target = ActionListState.Target.Loading(aTimelineItemEvent())), anActionListState().copy(target = ActionListState.Target.Loading(aTimelineItemEvent())),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent().copy( event = aTimelineItemEvent(
reactionsState = reactionsState timelineItemReactions = reactionsState
), ),
displayEmojiReactions = true, displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
) )
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent( event = aTimelineItemEvent(
content = aTimelineItemImageContent(), content = aTimelineItemImageContent(),
displayNameAmbiguous = true, displayNameAmbiguous = true,
).copy( timelineItemReactions = reactionsState,
reactionsState = reactionsState,
), ),
displayEmojiReactions = true, displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
) )
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent(content = aTimelineItemVideoContent()).copy( event = aTimelineItemEvent(
reactionsState = reactionsState content = aTimelineItemVideoContent(),
timelineItemReactions = reactionsState
), ),
displayEmojiReactions = true, displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
) )
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent(content = aTimelineItemFileContent()).copy( event = aTimelineItemEvent(
reactionsState = reactionsState content = aTimelineItemFileContent(),
timelineItemReactions = reactionsState
), ),
displayEmojiReactions = true, displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
) )
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent(content = aTimelineItemAudioContent()).copy( event = aTimelineItemEvent(
reactionsState = reactionsState content = aTimelineItemAudioContent(),
timelineItemReactions = reactionsState
), ),
displayEmojiReactions = true, displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
) )
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent(content = aTimelineItemVoiceContent()).copy( event = aTimelineItemEvent(
reactionsState = reactionsState content = aTimelineItemVoiceContent(),
timelineItemReactions = reactionsState
), ),
displayEmojiReactions = true, displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
) )
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent(content = aTimelineItemLocationContent()).copy( event = aTimelineItemEvent(
reactionsState = reactionsState content = aTimelineItemLocationContent(),
timelineItemReactions = reactionsState
), ),
displayEmojiReactions = true, displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
) )
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent(content = aTimelineItemLocationContent()).copy( event = aTimelineItemEvent(
reactionsState = reactionsState content = aTimelineItemLocationContent(),
timelineItemReactions = reactionsState
), ),
displayEmojiReactions = false, displayEmojiReactions = false,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
), ),
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent(content = aTimelineItemPollContent()).copy( event = aTimelineItemEvent(
reactionsState = reactionsState content = aTimelineItemPollContent(),
timelineItemReactions = reactionsState
), ),
displayEmojiReactions = false, displayEmojiReactions = false,
verifiedUserSendFailure = VerifiedUserSendFailure.None, verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemPollActionList(), actions = aTimelineItemPollActionList(),
), ),
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent().copy( event = aTimelineItemEvent(
reactionsState = reactionsState, timelineItemReactions = reactionsState,
messageShield = MessageShield.UnknownDevice(isCritical = true) messageShield = MessageShield.UnknownDevice(isCritical = true)
), ),
displayEmojiReactions = true, displayEmojiReactions = true,
@ -135,7 +141,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
actions = aTimelineItemActionList(), actions = aTimelineItemActionList(),
) )
), ),
anActionListState().copy( anActionListState(
target = ActionListState.Target.Success( target = ActionListState.Target.Success(
event = aTimelineItemEvent(), event = aTimelineItemEvent(),
displayEmojiReactions = true, displayEmojiReactions = true,

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt

@ -164,10 +164,10 @@ internal fun aTimelineItemEvent(
groupPosition = groupPosition, groupPosition = groupPosition,
localSendState = sendState, localSendState = sendState,
inReplyTo = inReplyTo, inReplyTo = inReplyTo,
debugInfoProvider = { debugInfo },
isThreaded = isThreaded, isThreaded = isThreaded,
origin = null, origin = null,
messageShield = messageShield, timelineItemDebugInfoProvider = { debugInfo },
messageShieldProvider = { messageShield },
) )
} }

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt

@ -26,7 +26,9 @@ internal fun TimelineViewMessageShieldPreview() = ElementPreview {
// For consistency, ensure that there is a message in the timeline (the last one) with an error. // For consistency, ensure that there is a message in the timeline (the last one) with an error.
val messageShield = aCriticalShield() val messageShield = aCriticalShield()
val items = listOf( val items = listOf(
(timelineItems.first() as TimelineItem.Event).copy(messageShield = messageShield) (timelineItems.first() as TimelineItem.Event).copy(
messageShieldProvider = { messageShield },
)
) + timelineItems.drop(1) ) + timelineItems.drop(1)
CompositionLocalProvider( CompositionLocalProvider(
LocalTimelineItemPresenterFactories provides aFakeTimelineItemPresenterFactories(), LocalTimelineItemPresenterFactories provides aFakeTimelineItemPresenterFactories(),

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt

@ -27,10 +27,10 @@ class TimelineItemEventForTimestampViewProvider : PreviewParameterProvider<Timel
localSendState = LocalEventSendState.Failed.Unknown("AN_ERROR"), localSendState = LocalEventSendState.Failed.Unknown("AN_ERROR"),
content = aTimelineItemTextContent().copy(isEdited = true), content = aTimelineItemTextContent().copy(isEdited = true),
), ),
aTimelineItemEvent().copy( aTimelineItemEvent(
messageShield = MessageShield.AuthenticityNotGuaranteed(isCritical = false), messageShield = MessageShield.AuthenticityNotGuaranteed(isCritical = false),
), ),
aTimelineItemEvent().copy( aTimelineItemEvent(
messageShield = MessageShield.UnknownDevice(isCritical = true), messageShield = MessageShield.UnknownDevice(isCritical = true),
), ),
) )

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt

@ -85,9 +85,9 @@ class TimelineItemEventFactory @AssistedInject constructor(
localSendState = currentTimelineItem.event.localSendState, localSendState = currentTimelineItem.event.localSendState,
inReplyTo = currentTimelineItem.event.inReplyTo()?.map(permalinkParser = permalinkParser), inReplyTo = currentTimelineItem.event.inReplyTo()?.map(permalinkParser = permalinkParser),
isThreaded = currentTimelineItem.event.isThreaded(), isThreaded = currentTimelineItem.event.isThreaded(),
debugInfoProvider = currentTimelineItem.event.debugInfoProvider,
origin = currentTimelineItem.event.origin, origin = currentTimelineItem.event.origin,
messageShield = currentTimelineItem.event.messageShieldProvider.getShield(strict = false) timelineItemDebugInfoProvider = currentTimelineItem.event.timelineItemDebugInfoProvider,
messageShieldProvider = currentTimelineItem.event.messageShieldProvider,
) )
} }

14
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt

@ -17,10 +17,12 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UniqueId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.EventDebugInfoProvider import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShieldProvider
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemDebugInfoProvider
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
@ -74,9 +76,9 @@ sealed interface TimelineItem {
val localSendState: LocalEventSendState?, val localSendState: LocalEventSendState?,
val inReplyTo: InReplyToDetails?, val inReplyTo: InReplyToDetails?,
val isThreaded: Boolean, val isThreaded: Boolean,
val debugInfoProvider: EventDebugInfoProvider,
val origin: TimelineItemEventOrigin?, val origin: TimelineItemEventOrigin?,
val messageShield: MessageShield?, val timelineItemDebugInfoProvider: TimelineItemDebugInfoProvider,
val messageShieldProvider: MessageShieldProvider,
) : TimelineItem { ) : TimelineItem {
val showSenderInformation = groupPosition.isNew() && !isMine val showSenderInformation = groupPosition.isNew() && !isMine
@ -90,7 +92,11 @@ sealed interface TimelineItem {
val isRemote = eventId != null val isRemote = eventId != null
val debugInfo = debugInfoProvider.get() // No need to be lazy here?
val messageShield: MessageShield? = messageShieldProvider(strict = false)
val debugInfo: TimelineItemDebugInfo
get() = timelineItemDebugInfoProvider()
} }
@Immutable @Immutable

10
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt

@ -35,12 +35,12 @@ interface VoiceMessageMediaRepo {
* *
* @param mediaSource the media source of the voice message. * @param mediaSource the media source of the voice message.
* @param mimeType the mime type of the voice message. * @param mimeType the mime type of the voice message.
* @param body the body of the voice message. * @param filename the filename of the voice message.
*/ */
fun create( fun create(
mediaSource: MediaSource, mediaSource: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
): VoiceMessageMediaRepo ): VoiceMessageMediaRepo
} }
@ -61,7 +61,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
private val matrixMediaLoader: MatrixMediaLoader, private val matrixMediaLoader: MatrixMediaLoader,
@Assisted private val mediaSource: MediaSource, @Assisted private val mediaSource: MediaSource,
@Assisted("mimeType") private val mimeType: String?, @Assisted("mimeType") private val mimeType: String?,
@Assisted("body") private val body: String?, @Assisted("filename") private val filename: String?,
) : VoiceMessageMediaRepo { ) : VoiceMessageMediaRepo {
@ContributesBinding(RoomScope::class) @ContributesBinding(RoomScope::class)
@AssistedFactory @AssistedFactory
@ -69,7 +69,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
override fun create( override fun create(
mediaSource: MediaSource, mediaSource: MediaSource,
@Assisted("mimeType") mimeType: String?, @Assisted("mimeType") mimeType: String?,
@Assisted("body") body: String?, @Assisted("filename") filename: String?,
): DefaultVoiceMessageMediaRepo ): DefaultVoiceMessageMediaRepo
} }
@ -79,7 +79,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
else -> matrixMediaLoader.downloadMediaFile( else -> matrixMediaLoader.downloadMediaFile(
source = mediaSource, source = mediaSource,
mimeType = mimeType, mimeType = mimeType,
body = body, filename = filename,
).mapCatching { ).mapCatching {
it.use { mediaFile -> it.use { mediaFile ->
val dest = cachedFile.apply { parentFile?.mkdirs() } val dest = cachedFile.apply { parentFile?.mkdirs() }

12
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt

@ -37,13 +37,13 @@ interface VoiceMessagePlayer {
* @param eventId The eventId of the voice message event. * @param eventId The eventId of the voice message event.
* @param mediaSource The media source of the voice message. * @param mediaSource The media source of the voice message.
* @param mimeType The mime type of the voice message. * @param mimeType The mime type of the voice message.
* @param body The body of the voice message. * @param filename The filename of the voice message.
*/ */
fun create( fun create(
eventId: EventId?, eventId: EventId?,
mediaSource: MediaSource, mediaSource: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
): VoiceMessagePlayer ): VoiceMessagePlayer
} }
@ -113,7 +113,7 @@ class DefaultVoiceMessagePlayer(
private val eventId: EventId?, private val eventId: EventId?,
mediaSource: MediaSource, mediaSource: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
) : VoiceMessagePlayer { ) : VoiceMessagePlayer {
@ContributesBinding(RoomScope::class) // Scoped types can't use @AssistedInject. @ContributesBinding(RoomScope::class) // Scoped types can't use @AssistedInject.
class Factory @Inject constructor( class Factory @Inject constructor(
@ -124,21 +124,21 @@ class DefaultVoiceMessagePlayer(
eventId: EventId?, eventId: EventId?,
mediaSource: MediaSource, mediaSource: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
): DefaultVoiceMessagePlayer = DefaultVoiceMessagePlayer( ): DefaultVoiceMessagePlayer = DefaultVoiceMessagePlayer(
mediaPlayer = mediaPlayer, mediaPlayer = mediaPlayer,
voiceMessageMediaRepoFactory = voiceMessageMediaRepoFactory, voiceMessageMediaRepoFactory = voiceMessageMediaRepoFactory,
eventId = eventId, eventId = eventId,
mediaSource = mediaSource, mediaSource = mediaSource,
mimeType = mimeType, mimeType = mimeType,
body = body, filename = filename,
) )
} }
private val repo = voiceMessageMediaRepoFactory.create( private val repo = voiceMessageMediaRepoFactory.create(
mediaSource = mediaSource, mediaSource = mediaSource,
mimeType = mimeType, mimeType = mimeType,
body = body filename = filename,
) )
private var internalState = MutableStateFlow( private var internalState = MutableStateFlow(

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt

@ -59,7 +59,7 @@ class VoiceMessagePresenter @AssistedInject constructor(
eventId = content.eventId, eventId = content.eventId,
mediaSource = content.mediaSource, mediaSource = content.mediaSource,
mimeType = content.mimeType, mimeType = content.mimeType,
body = content.caption, filename = content.filename,
) )
private val play = mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized) private val play = mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized)

4
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt

@ -58,8 +58,8 @@ internal fun aMessageEvent(
readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()), readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()),
localSendState = sendState, localSendState = sendState,
inReplyTo = inReplyTo, inReplyTo = inReplyTo,
debugInfoProvider = { debugInfo },
isThreaded = isThreaded, isThreaded = isThreaded,
origin = null, origin = null,
messageShield = messageShield, timelineItemDebugInfoProvider = { debugInfo },
messageShieldProvider = { messageShield },
) )

4
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt

@ -41,9 +41,9 @@ class TimelineItemGrouperTest {
canBeRepliedTo = false, canBeRepliedTo = false,
inReplyTo = null, inReplyTo = null,
isThreaded = false, isThreaded = false,
debugInfoProvider = { aTimelineItemDebugInfo() },
origin = null, origin = null,
messageShield = null, timelineItemDebugInfoProvider = { aTimelineItemDebugInfo() },
messageShieldProvider = { null },
) )
private val aNonGroupableItem = aMessageEvent() private val aNonGroupableItem = aMessageEvent()
private val aNonGroupableItemNoEvent = TimelineItem.Virtual(UniqueId("virtual"), aTimelineItemDaySeparatorModel("Today")) private val aNonGroupableItemNoEvent = TimelineItem.Virtual(UniqueId("virtual"), aTimelineItemDaySeparatorModel("Today"))

2
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt

@ -137,7 +137,7 @@ private fun createDefaultVoiceMessageMediaRepo(
json = null json = null
), ),
mimeType = MimeTypes.Ogg, mimeType = MimeTypes.Ogg,
body = "someBody.ogg" filename = "someBody.ogg"
) )
private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg" private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg"

2
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt

@ -279,7 +279,7 @@ private fun createDefaultVoiceMessagePlayer(
json = null json = null
), ),
mimeType = MimeTypes.Ogg, mimeType = MimeTypes.Ogg,
body = "someBody.ogg" filename = "someBody.ogg"
) )
private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg" private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg"

7
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt

@ -78,7 +78,6 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf<MatrixTimelineItem>(
transactionId = null, transactionId = null,
isEditable = false, isEditable = false,
canBeRepliedTo = false, canBeRepliedTo = false,
isLocal = false,
isOwn = false, isOwn = false,
isRemote = false, isRemote = false,
localSendState = null, localSendState = null,
@ -88,14 +87,14 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf<MatrixTimelineItem>(
senderProfile = ProfileTimelineDetails.Unavailable, senderProfile = ProfileTimelineDetails.Unavailable,
timestamp = 9442, timestamp = 9442,
content = RedactedContent, content = RedactedContent,
debugInfoProvider = { origin = null,
timelineItemDebugInfoProvider = {
TimelineItemDebugInfo( TimelineItemDebugInfo(
model = "enim", model = "enim",
originalJson = null, originalJson = null,
latestEditedJson = null latestEditedJson = null,
) )
}, },
origin = null,
messageShieldProvider = { null }, messageShieldProvider = { null },
), ),
) )

4
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt

@ -226,14 +226,14 @@ fun TestScope.createVoiceMessagePresenter(
analyticsService: AnalyticsService = FakeAnalyticsService(), analyticsService: AnalyticsService = FakeAnalyticsService(),
content: TimelineItemVoiceContent = aTimelineItemVoiceContent(), content: TimelineItemVoiceContent = aTimelineItemVoiceContent(),
) = VoiceMessagePresenter( ) = VoiceMessagePresenter(
voiceMessagePlayerFactory = { eventId, mediaSource, mimeType, body -> voiceMessagePlayerFactory = { eventId, mediaSource, mimeType, filename ->
DefaultVoiceMessagePlayer( DefaultVoiceMessagePlayer(
mediaPlayer = mediaPlayer, mediaPlayer = mediaPlayer,
voiceMessageMediaRepoFactory = { _, _, _ -> voiceMessageMediaRepo }, voiceMessageMediaRepoFactory = { _, _, _ -> voiceMessageMediaRepo },
eventId = eventId, eventId = eventId,
mediaSource = mediaSource, mediaSource = mediaSource,
mimeType = mimeType, mimeType = mimeType,
body = body filename = filename
) )
}, },
analyticsService = analyticsService, analyticsService = analyticsService,

2
gradle/libs.versions.toml

@ -168,7 +168,7 @@ jsoup = "org.jsoup:jsoup:1.18.1"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
timber = "com.jakewharton.timber:timber:5.0.1" timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.53" matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.54"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt

@ -43,7 +43,7 @@ interface MatrixAuthenticationService {
/** /**
* Get the Oidc url to display to the user. * Get the Oidc url to display to the user.
*/ */
suspend fun getOidcUrl(): Result<OidcDetails> suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails>
/** /**
* Cancel Oidc login sequence. * Cancel Oidc login sequence.

51
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt

@ -0,0 +1,51 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.auth
sealed interface OidcPrompt {
/**
* The Authorization Server must not display any authentication or consent
* user interface pages.
*/
data object None : OidcPrompt
/**
* The Authorization Server should prompt the End-User for
* reauthentication.
*/
data object Login : OidcPrompt
/**
* The Authorization Server should prompt the End-User for consent before
* returning information to the Client.
*/
data object Consent : OidcPrompt
/**
* The Authorization Server should prompt the End-User to select a user
* account.
*
* This enables an End-User who has multiple accounts at the Authorization
* Server to select amongst the multiple accounts that they might have
* current sessions for.
*/
data object SelectAccount : OidcPrompt
/**
* The Authorization Server should prompt the End-User to create a user
* account.
*
* Defined in [Initiating User Registration via OpenID Connect](https://openid.net/specs/openid-connect-prompt-create-1_0.html).
*/
data object Create : OidcPrompt
/**
* An unknown value.
*/
data class Unknown(val value: String) : OidcPrompt
}

4
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt

@ -25,14 +25,14 @@ interface MatrixMediaLoader {
/** /**
* @param source to fetch the data for. * @param source to fetch the data for.
* @param mimeType: optional mime type. * @param mimeType: optional mime type.
* @param body: optional body which will be used to name the file. * @param filename: optional String which will be used to name the file.
* @param useCache: if true, the rust sdk will cache the media in its store. * @param useCache: if true, the rust sdk will cache the media in its store.
* @return a [Result] of [MediaFile] * @return a [Result] of [MediaFile]
*/ */
suspend fun downloadMediaFile( suspend fun downloadMediaFile(
source: MediaSource, source: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
useCache: Boolean = true, useCache: Boolean = true,
): Result<MediaFile> ): Result<MediaFile>
} }

3
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt

@ -10,5 +10,6 @@ package io.element.android.libraries.matrix.api.room
enum class CurrentUserMembership { enum class CurrentUserMembership {
INVITED, INVITED,
JOINED, JOINED,
LEFT LEFT,
KNOCKED,
} }

13
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt

@ -18,7 +18,6 @@ data class EventTimelineItem(
val transactionId: TransactionId?, val transactionId: TransactionId?,
val isEditable: Boolean, val isEditable: Boolean,
val canBeRepliedTo: Boolean, val canBeRepliedTo: Boolean,
val isLocal: Boolean,
val isOwn: Boolean, val isOwn: Boolean,
val isRemote: Boolean, val isRemote: Boolean,
val localSendState: LocalEventSendState?, val localSendState: LocalEventSendState?,
@ -28,9 +27,9 @@ data class EventTimelineItem(
val senderProfile: ProfileTimelineDetails, val senderProfile: ProfileTimelineDetails,
val timestamp: Long, val timestamp: Long,
val content: EventContent, val content: EventContent,
val debugInfoProvider: EventDebugInfoProvider,
val origin: TimelineItemEventOrigin?, val origin: TimelineItemEventOrigin?,
val messageShieldProvider: EventShieldsProvider, val timelineItemDebugInfoProvider: TimelineItemDebugInfoProvider,
val messageShieldProvider: MessageShieldProvider,
) { ) {
fun inReplyTo(): InReplyTo? { fun inReplyTo(): InReplyTo? {
return (content as? MessageContent)?.inReplyTo return (content as? MessageContent)?.inReplyTo
@ -46,10 +45,10 @@ data class EventTimelineItem(
} }
} }
fun interface EventDebugInfoProvider { fun interface TimelineItemDebugInfoProvider {
fun get(): TimelineItemDebugInfo operator fun invoke(): TimelineItemDebugInfo
} }
fun interface EventShieldsProvider { fun interface MessageShieldProvider {
fun getShield(strict: Boolean): MessageShield? operator fun invoke(strict: Boolean): MessageShield?
} }

22
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt

@ -0,0 +1,22 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.auth
import io.element.android.libraries.matrix.api.auth.OidcPrompt
import org.matrix.rustcomponents.sdk.OidcPrompt as RustOidcPrompt
internal fun OidcPrompt.toRustPrompt(): RustOidcPrompt {
return when (this) {
OidcPrompt.None -> RustOidcPrompt.None
OidcPrompt.Login -> RustOidcPrompt.Login
OidcPrompt.Consent -> RustOidcPrompt.Consent
OidcPrompt.SelectAccount -> RustOidcPrompt.SelectAccount
OidcPrompt.Create -> RustOidcPrompt.Create
is OidcPrompt.Unknown -> RustOidcPrompt.Unknown(value)
}
}

8
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt

@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.auth.OidcPrompt
import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.external.ExternalSession
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
@ -181,11 +182,14 @@ class RustMatrixAuthenticationService @Inject constructor(
private var pendingOidcAuthorizationData: OidcAuthorizationData? = null private var pendingOidcAuthorizationData: OidcAuthorizationData? = null
override suspend fun getOidcUrl(): Result<OidcDetails> { override suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails> {
return withContext(coroutineDispatchers.io) { return withContext(coroutineDispatchers.io) {
runCatching { runCatching {
val client = currentClient ?: error("You need to call `setHomeserver()` first") val client = currentClient ?: error("You need to call `setHomeserver()` first")
val oidcAuthenticationData = client.urlForOidcLogin(oidcConfigurationProvider.get()) val oidcAuthenticationData = client.urlForOidc(
oidcConfiguration = oidcConfigurationProvider.get(),
prompt = prompt.toRustPrompt(),
)
val url = oidcAuthenticationData.loginUrl() val url = oidcAuthenticationData.loginUrl()
pendingOidcAuthorizationData = oidcAuthenticationData pendingOidcAuthorizationData = oidcAuthenticationData
OidcDetails(url) OidcDetails(url)

4
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt

@ -63,7 +63,7 @@ class RustMediaLoader(
override suspend fun downloadMediaFile( override suspend fun downloadMediaFile(
source: MediaSource, source: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
useCache: Boolean, useCache: Boolean,
): Result<MediaFile> = ): Result<MediaFile> =
withContext(mediaDispatcher) { withContext(mediaDispatcher) {
@ -71,7 +71,7 @@ class RustMediaLoader(
source.toRustMediaSource().use { mediaSource -> source.toRustMediaSource().use { mediaSource ->
val mediaFile = innerClient.getMediaFile( val mediaFile = innerClient.getMediaFile(
mediaSource = mediaSource, mediaSource = mediaSource,
body = body, filename = filename,
mimeType = mimeType?.takeIf { MimeTypes.hasSubtype(it) } ?: MimeTypes.OctetStream, mimeType = mimeType?.takeIf { MimeTypes.hasSubtype(it) } ?: MimeTypes.OctetStream,
useCache = useCache, useCache = useCache,
tempDir = cacheDirectory.path, tempDir = cacheDirectory.path,

2
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt

@ -19,6 +19,7 @@ import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentMap import kotlinx.collections.immutable.toPersistentMap
import org.matrix.rustcomponents.sdk.Membership
import org.matrix.rustcomponents.sdk.RoomHero import org.matrix.rustcomponents.sdk.RoomHero
import org.matrix.rustcomponents.sdk.Membership as RustMembership import org.matrix.rustcomponents.sdk.Membership as RustMembership
import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo
@ -65,6 +66,7 @@ fun RustMembership.map(): CurrentUserMembership = when (this) {
RustMembership.INVITED -> CurrentUserMembership.INVITED RustMembership.INVITED -> CurrentUserMembership.INVITED
RustMembership.JOINED -> CurrentUserMembership.JOINED RustMembership.JOINED -> CurrentUserMembership.JOINED
RustMembership.LEFT -> CurrentUserMembership.LEFT RustMembership.LEFT -> CurrentUserMembership.LEFT
Membership.KNOCKED -> CurrentUserMembership.KNOCKED
} }
fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) { fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) {

21
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt

@ -12,9 +12,7 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.api.timeline.item.event.EventDebugInfoProvider
import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
import io.element.android.libraries.matrix.api.timeline.item.event.EventShieldsProvider
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
@ -27,12 +25,10 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import org.matrix.rustcomponents.sdk.EventOrTransactionId import org.matrix.rustcomponents.sdk.EventOrTransactionId
import org.matrix.rustcomponents.sdk.EventSendState import org.matrix.rustcomponents.sdk.EventSendState
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfoProvider
import org.matrix.rustcomponents.sdk.Reaction import org.matrix.rustcomponents.sdk.Reaction
import org.matrix.rustcomponents.sdk.ShieldState import org.matrix.rustcomponents.sdk.ShieldState
import uniffi.matrix_sdk_common.ShieldStateCode import uniffi.matrix_sdk_common.ShieldStateCode
import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState
import org.matrix.rustcomponents.sdk.EventShieldsProvider as RustEventShieldsProvider
import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo as RustEventTimelineItemDebugInfo import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo as RustEventTimelineItemDebugInfo
import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails
@ -48,7 +44,6 @@ class EventTimelineItemMapper(
transactionId = eventOrTransactionId.transactionId(), transactionId = eventOrTransactionId.transactionId(),
isEditable = isEditable, isEditable = isEditable,
canBeRepliedTo = canBeRepliedTo, canBeRepliedTo = canBeRepliedTo,
isLocal = isLocal,
isOwn = isOwn, isOwn = isOwn,
isRemote = isRemote, isRemote = isRemote,
localSendState = localSendState?.map(), localSendState = localSendState?.map(),
@ -58,9 +53,9 @@ class EventTimelineItemMapper(
senderProfile = senderProfile.map(), senderProfile = senderProfile.map(),
timestamp = timestamp.toLong(), timestamp = timestamp.toLong(),
content = contentMapper.map(content), content = contentMapper.map(content),
debugInfoProvider = RustEventDebugInfoProvider(debugInfoProvider),
origin = origin?.map(), origin = origin?.map(),
messageShieldProvider = RustEventShieldsProvider(shieldsProvider) timelineItemDebugInfoProvider = { lazyProvider.debugInfo().map() },
messageShieldProvider = { strict -> lazyProvider.getShields(strict)?.map() },
) )
} }
} }
@ -168,18 +163,6 @@ private fun ShieldState?.map(): MessageShield? {
} }
} }
class RustEventDebugInfoProvider(private val debugInfoProvider: EventTimelineItemDebugInfoProvider) : EventDebugInfoProvider {
override fun get(): TimelineItemDebugInfo {
return debugInfoProvider.get().map()
}
}
class RustEventShieldsProvider(private val shieldsProvider: RustEventShieldsProvider) : EventShieldsProvider {
override fun getShield(strict: Boolean): MessageShield? {
return shieldsProvider.getShields(strict)?.map()
}
}
private fun EventOrTransactionId.eventId(): EventId? { private fun EventOrTransactionId.eventId(): EventId? {
return (this as? EventOrTransactionId.EventId)?.let { EventId(it.eventId) } return (this as? EventOrTransactionId.EventId)?.let { EventId(it.eventId) }
} }

11
libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt

@ -7,8 +7,7 @@
package io.element.android.libraries.matrix.impl.fixtures.factories package io.element.android.libraries.matrix.impl.fixtures.factories
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustEventShieldsProvider import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustLazyTimelineItemProvider
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustEventTimelineItemDebugInfoProvider
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID
import org.matrix.rustcomponents.sdk.EventOrTransactionId import org.matrix.rustcomponents.sdk.EventOrTransactionId
@ -23,7 +22,6 @@ import org.matrix.rustcomponents.sdk.TimelineItemContent
import uniffi.matrix_sdk_ui.EventItemOrigin import uniffi.matrix_sdk_ui.EventItemOrigin
fun aRustEventTimelineItem( fun aRustEventTimelineItem(
isLocal: Boolean = false,
isRemote: Boolean = true, isRemote: Boolean = true,
eventOrTransactionId: EventOrTransactionId = EventOrTransactionId.EventId(AN_EVENT_ID.value), eventOrTransactionId: EventOrTransactionId = EventOrTransactionId.EventId(AN_EVENT_ID.value),
sender: String = A_USER_ID.value, sender: String = A_USER_ID.value,
@ -40,7 +38,6 @@ fun aRustEventTimelineItem(
canBeRepliedTo: Boolean = true, canBeRepliedTo: Boolean = true,
shieldsState: ShieldState? = null, shieldsState: ShieldState? = null,
) = EventTimelineItem( ) = EventTimelineItem(
isLocal = isLocal,
isRemote = isRemote, isRemote = isRemote,
eventOrTransactionId = eventOrTransactionId, eventOrTransactionId = eventOrTransactionId,
sender = sender, sender = sender,
@ -50,10 +47,12 @@ fun aRustEventTimelineItem(
isEditable = isEditable, isEditable = isEditable,
canBeRepliedTo = canBeRepliedTo, canBeRepliedTo = canBeRepliedTo,
content = content, content = content,
debugInfoProvider = FakeRustEventTimelineItemDebugInfoProvider(debugInfo),
shieldsProvider = FakeRustEventShieldsProvider(shieldsState),
localSendState = localSendState, localSendState = localSendState,
reactions = reactions, reactions = reactions,
readReceipts = readReceipts, readReceipts = readReceipts,
origin = origin, origin = origin,
lazyProvider = FakeRustLazyTimelineItemProvider(
debugInfo = debugInfo,
shieldsState = shieldsState,
)
) )

18
libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt

@ -1,18 +0,0 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.fixtures.fakes
import org.matrix.rustcomponents.sdk.EventShieldsProvider
import org.matrix.rustcomponents.sdk.NoPointer
import org.matrix.rustcomponents.sdk.ShieldState
class FakeRustEventShieldsProvider(
private val shieldsState: ShieldState? = null,
) : EventShieldsProvider(NoPointer) {
override fun getShields(strict: Boolean): ShieldState? = shieldsState
}

11
libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventTimelineItemDebugInfoProvider.kt → libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustLazyTimelineItemProvider.kt

@ -9,11 +9,14 @@ package io.element.android.libraries.matrix.impl.fixtures.fakes
import io.element.android.libraries.matrix.impl.fixtures.factories.anEventTimelineItemDebugInfo import io.element.android.libraries.matrix.impl.fixtures.factories.anEventTimelineItemDebugInfo
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfoProvider import org.matrix.rustcomponents.sdk.LazyTimelineItemProvider
import org.matrix.rustcomponents.sdk.NoPointer import org.matrix.rustcomponents.sdk.NoPointer
import org.matrix.rustcomponents.sdk.ShieldState
class FakeRustEventTimelineItemDebugInfoProvider( class FakeRustLazyTimelineItemProvider(
private val debugInfo: EventTimelineItemDebugInfo = anEventTimelineItemDebugInfo(), private val debugInfo: EventTimelineItemDebugInfo = anEventTimelineItemDebugInfo(),
) : EventTimelineItemDebugInfoProvider(NoPointer) { private val shieldsState: ShieldState? = null,
override fun get(): EventTimelineItemDebugInfo = debugInfo ) : LazyTimelineItemProvider(NoPointer) {
override fun getShields(strict: Boolean) = shieldsState
override fun debugInfo() = debugInfo
} }

3
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt

@ -11,6 +11,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.auth.OidcPrompt
import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.external.ExternalSession
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
@ -80,7 +81,7 @@ class FakeMatrixAuthenticationService(
return importCreatedSessionLambda(externalSession) return importCreatedSessionLambda(externalSession)
} }
override suspend fun getOidcUrl(): Result<OidcDetails> = simulateLongTask { override suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails> = simulateLongTask {
oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA) oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA)
} }

2
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt

@ -35,7 +35,7 @@ class FakeMatrixMediaLoader : MatrixMediaLoader {
override suspend fun downloadMediaFile( override suspend fun downloadMediaFile(
source: MediaSource, source: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
useCache: Boolean, useCache: Boolean,
): Result<MediaFile> = simulateLongTask { ): Result<MediaFile> = simulateLongTask {
if (shouldFail) { if (shouldFail) {

4
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt

@ -42,7 +42,6 @@ fun anEventTimelineItem(
transactionId: TransactionId? = null, transactionId: TransactionId? = null,
isEditable: Boolean = false, isEditable: Boolean = false,
canBeRepliedTo: Boolean = false, canBeRepliedTo: Boolean = false,
isLocal: Boolean = false,
isOwn: Boolean = false, isOwn: Boolean = false,
isRemote: Boolean = false, isRemote: Boolean = false,
localSendState: LocalEventSendState? = null, localSendState: LocalEventSendState? = null,
@ -59,7 +58,6 @@ fun anEventTimelineItem(
transactionId = transactionId, transactionId = transactionId,
isEditable = isEditable, isEditable = isEditable,
canBeRepliedTo = canBeRepliedTo, canBeRepliedTo = canBeRepliedTo,
isLocal = isLocal,
isOwn = isOwn, isOwn = isOwn,
isRemote = isRemote, isRemote = isRemote,
localSendState = localSendState, localSendState = localSendState,
@ -69,8 +67,8 @@ fun anEventTimelineItem(
senderProfile = senderProfile, senderProfile = senderProfile,
timestamp = timestamp, timestamp = timestamp,
content = content, content = content,
debugInfoProvider = { debugInfo },
origin = null, origin = null,
timelineItemDebugInfoProvider = { debugInfo },
messageShieldProvider = { messageShield }, messageShieldProvider = { messageShield },
) )

2
libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt

@ -92,7 +92,7 @@ class MediaViewerPresenter @AssistedInject constructor(
mediaLoader.downloadMediaFile( mediaLoader.downloadMediaFile(
source = inputs.mediaSource, source = inputs.mediaSource,
mimeType = inputs.mediaInfo.mimeType, mimeType = inputs.mediaInfo.mimeType,
body = inputs.mediaInfo.filename filename = inputs.mediaInfo.filename
) )
.onSuccess { .onSuccess {
mediaFile.value = it mediaFile.value = it

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt

@ -299,7 +299,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
.getMediaFile( .getMediaFile(
mediaSource = messageType.source, mediaSource = messageType.source,
mimeType = messageType.info?.mimetype, mimeType = messageType.info?.mimetype,
body = messageType.filename, filename = messageType.filename,
) )
is VideoMessageType -> null // Use the thumbnail here? is VideoMessageType -> null // Use the thumbnail here?
else -> null else -> null

8
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt

@ -47,13 +47,13 @@ interface NotificationMediaRepo {
* *
* @param mediaSource the media source of the media. * @param mediaSource the media source of the media.
* @param mimeType the mime type of the media. * @param mimeType the mime type of the media.
* @param body optional body which will be used to name the file. * @param filename optional String which will be used to name the file.
* @return A [Result] holding either the media [File] from the cache directory or an [Exception]. * @return A [Result] holding either the media [File] from the cache directory or an [Exception].
*/ */
suspend fun getMediaFile( suspend fun getMediaFile(
mediaSource: MediaSource, mediaSource: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
): Result<File> ): Result<File>
} }
@ -75,7 +75,7 @@ class DefaultNotificationMediaRepo @AssistedInject constructor(
override suspend fun getMediaFile( override suspend fun getMediaFile(
mediaSource: MediaSource, mediaSource: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
): Result<File> { ): Result<File> {
val cachedFile = mediaSource.cachedFile() val cachedFile = mediaSource.cachedFile()
return when { return when {
@ -84,7 +84,7 @@ class DefaultNotificationMediaRepo @AssistedInject constructor(
else -> matrixMediaLoader.downloadMediaFile( else -> matrixMediaLoader.downloadMediaFile(
source = mediaSource, source = mediaSource,
mimeType = mimeType, mimeType = mimeType,
body = body, filename = filename,
).mapCatching { ).mapCatching {
it.use { mediaFile -> it.use { mediaFile ->
val dest = cachedFile.apply { parentFile?.mkdirs() } val dest = cachedFile.apply { parentFile?.mkdirs() }

2
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt

@ -15,7 +15,7 @@ class FakeNotificationMediaRepo : NotificationMediaRepo {
override suspend fun getMediaFile( override suspend fun getMediaFile(
mediaSource: MediaSource, mediaSource: MediaSource,
mimeType: String?, mimeType: String?,
body: String?, filename: String?,
): Result<File> { ): Result<File> {
return Result.failure(IllegalStateException("Fake class")) return Result.failure(IllegalStateException("Fake class"))
} }

Loading…
Cancel
Save