Repository: element-hq/element-x-android (language: kotlin)

Issue:
When creating an account with MAS, change the `prompt` parameter to `create`

<!-- Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
 
## Content

<!-- Describe shortly what has been changed -->
When user is creating account, load the url page directly in the account creation mode by changing (or adding) the `prompt` url parameter to `create`.

@sandhose does it make sense to you?

## Motivation and context

<!-- Provide link to the corresponding issue if applicable or explain the context -->
Improve user experience with MAS and closes #3627

## Screenshots / GIFs

<!--
We have screenshot tests in the project, so attaching screenshots to a PR is not mandatory, as far as there
is a Composable Preview covering the changes. In this case, the change will appear in the file diff.
Note that all the UI composables should be covered by a Composable Preview.

Providing a video of the change is still very useful for the reviewer and for the history of the project.

You can use a table like this to show screenshots comparison.
Uncomment this markdown table below and edit the last line `|||`:
|copy screenshot of before here|copy screenshot of after here|

 -->

|Before|After|
|-|-|
|<video width="98" alt="image" src="https://github.com/user-attachments/assets/ff90a0aa-a776-43fd-ad15-eb2160c91e78" />|<video width="98" alt="image" src="https://github.com/user-attachments/assets/e7d8f605-9f18-45d6-bd29-69ee597f2272"/>|

## Tests

<!-- Explain how you tested your development -->

- Try creating an account on a server with MAS support, such as beta.matrix.org and observe that you're having a wonderful navigation experience.

## Tested devices

- [ ] Physical
- [x] Emulator
- OS version(s):

## Checklist

<!-- Depending on the Pull Request content, it can be acceptable if some of the following checkboxes stay unchecked. -->

- [ ] Changes have been tested on an Android device or Android emulator with API 23
- [ ] UI change has been tested on both light and dark themes
- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
- [ ] Pull request is based on the develop branch
- [ ] Pull request title will be used in the release note, it clearly define what will change for the user
- [ ] Pull request includes screenshots or videos if containing UI changes
- [ ] Pull request includes a [sign off](https://matrix-org.github.io/synapse/latest/development/contributing_guide.html#sign-off)
- [ ] You've made a self review of your PR



Discussion hints:
<!-- GITHUB_COMMENT_QR_CODE -->
:iphone: Scan the QR code below to install the build (arm64 only) for this PR.
![QR code](https://www.diawi.com/qrcode/link/V8Ep1n)
If you can't scan the QR code you can install the build via this link: https://i.diawi.com/V8Ep1n
## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=element-x-android&pullRequest=3635) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=element-x-android&pullRequest=3635&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=element-x-android&pullRequest=3635&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=element-x-android&pullRequest=3635&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=element-x-android&pullRequest=3635&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=element-x-android&pullRequest=3635&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=element-x-android&pullRequest=3635)


OK so maybe we could improve the existing Rust API, to replace `urlForOidcLogin()` by `urlForOidc(prompt: String)`, or simply add an API `urlForOidcRegister()`

@jmartinesp do you think this would be possible?
From a very quick look at the code, it seems feasible, although I'd rather make the `Prompt` an enum in the FFI layer so the client can't just pass any value. What do you think?
Yes, an enum would be better than a String. Thanks!
Closing this we will get a better way to handle this thanks to Jorge's work in https://github.com/matrix-org/matrix-rust-sdk/pull/4131.

Pull request: OIDC prompt
<!-- Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
 
## Content

<!-- Describe shortly what has been changed -->
SDK 0.1.54 and fix all the API break.

Then last commit fixes #3635 (so this PR replaces #3635)

## Motivation and context

<!-- Provide link to the corresponding issue if applicable or explain the context -->
Use latest SDK and fix a UX issue.

## Screenshots / GIFs

<!--
We have screenshot tests in the project, so attaching screenshots to a PR is not mandatory, as far as there
is a Composable Preview covering the changes. In this case, the change will appear in the file diff.
Note that all the UI composables should be covered by a Composable Preview.

Providing a video of the change is still very useful for the reviewer and for the history of the project.

You can use a table like this to show screenshots comparison.
Uncomment this markdown table below and edit the last line `|||`:
|copy screenshot of before here|copy screenshot of after here|

|Before|After|
|-|-|
|||
 -->

## Tests

<!-- Explain how you tested your development -->

- Step 1
- Step 2
- Step ...

## Tested devices

- [ ] Physical
- [ ] Emulator
- OS version(s):

## Checklist

<!-- Depending on the Pull Request content, it can be acceptable if some of the following checkboxes stay unchecked. -->

- [ ] Changes have been tested on an Android device or Android emulator with API 23
- [ ] UI change has been tested on both light and dark themes
- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
- [ ] Pull request is based on the develop branch
- [ ] Pull request title will be used in the release note, it clearly define what will change for the user
- [ ] Pull request includes screenshots or videos if containing UI changes
- [ ] Pull request includes a [sign off](https://matrix-org.github.io/synapse/latest/development/contributing_guide.html#sign-off)
- [ ] You've made a self review of your PR


Test command: ./gradlew :features:messages:impl:testDebugUnitTest --tests "io.element.android.features.messages.impl.voicemessages.timeline.DefaultVoiceMessageMediaRepoTest" --tests "io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManagerTest" --tests "io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessagePresenterTest" --tests "io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouperTest" --tests "io.element.android.features.messages.impl.voicemessages.timeline.DefaultVoiceMessagePlayerTest"

Fix patch:
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
index c15c84744ff..6cc4cdc5e4d 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
+++ b/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.runCatchingUpdatingState
 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.OidcActionFlow
 import kotlinx.coroutines.CoroutineScope
@@ -92,7 +93,8 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
                 val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!!
                 if (matrixHomeServerDetails.supportsOidcLogin) {
                     // 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) {
                     val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl)
                     LoginFlow.AccountCreationFlow(url)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt
index 4f92bc72ba3..78e65c839a7 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt
+++ b/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(
                 anActionListState(),
                 anActionListState().copy(target = ActionListState.Target.Loading(aTimelineItemEvent())),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent().copy(
-                            reactionsState = reactionsState
+                        event = aTimelineItemEvent(
+                            timelineItemReactions = reactionsState
                         ),
                         displayEmojiReactions = true,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemActionList(),
                     )
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
                         event = aTimelineItemEvent(
                             content = aTimelineItemImageContent(),
                             displayNameAmbiguous = true,
-                        ).copy(
-                            reactionsState = reactionsState,
+                            timelineItemReactions = reactionsState,
                         ),
                         displayEmojiReactions = true,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemActionList(),
                     )
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent(content = aTimelineItemVideoContent()).copy(
-                            reactionsState = reactionsState
+                        event = aTimelineItemEvent(
+                            content = aTimelineItemVideoContent(),
+                            timelineItemReactions = reactionsState
                         ),
                         displayEmojiReactions = true,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemActionList(),
                     )
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent(content = aTimelineItemFileContent()).copy(
-                            reactionsState = reactionsState
+                        event = aTimelineItemEvent(
+                            content = aTimelineItemFileContent(),
+                            timelineItemReactions = reactionsState
                         ),
                         displayEmojiReactions = true,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemActionList(),
                     )
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent(content = aTimelineItemAudioContent()).copy(
-                            reactionsState = reactionsState
+                        event = aTimelineItemEvent(
+                            content = aTimelineItemAudioContent(),
+                            timelineItemReactions = reactionsState
                         ),
                         displayEmojiReactions = true,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemActionList(),
                     )
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent(content = aTimelineItemVoiceContent()).copy(
-                            reactionsState = reactionsState
+                        event = aTimelineItemEvent(
+                            content = aTimelineItemVoiceContent(),
+                            timelineItemReactions = reactionsState
                         ),
                         displayEmojiReactions = true,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemActionList(),
                     )
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent(content = aTimelineItemLocationContent()).copy(
-                            reactionsState = reactionsState
+                        event = aTimelineItemEvent(
+                            content = aTimelineItemLocationContent(),
+                            timelineItemReactions = reactionsState
                         ),
                         displayEmojiReactions = true,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemActionList(),
                     )
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent(content = aTimelineItemLocationContent()).copy(
-                            reactionsState = reactionsState
+                        event = aTimelineItemEvent(
+                            content = aTimelineItemLocationContent(),
+                            timelineItemReactions = reactionsState
                         ),
                         displayEmojiReactions = false,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemActionList(),
                     ),
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent(content = aTimelineItemPollContent()).copy(
-                            reactionsState = reactionsState
+                        event = aTimelineItemEvent(
+                            content = aTimelineItemPollContent(),
+                            timelineItemReactions = reactionsState
                         ),
                         displayEmojiReactions = false,
                         verifiedUserSendFailure = VerifiedUserSendFailure.None,
                         actions = aTimelineItemPollActionList(),
                     ),
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
-                        event = aTimelineItemEvent().copy(
-                            reactionsState = reactionsState,
+                        event = aTimelineItemEvent(
+                            timelineItemReactions = reactionsState,
                             messageShield = MessageShield.UnknownDevice(isCritical = true)
                         ),
                         displayEmojiReactions = true,
@@ -135,7 +141,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
                         actions = aTimelineItemActionList(),
                     )
                 ),
-                anActionListState().copy(
+                anActionListState(
                     target = ActionListState.Target.Success(
                         event = aTimelineItemEvent(),
                         displayEmojiReactions = true,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
index fb96e75e281..6c790d7b1d8 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
@@ -164,10 +164,10 @@ internal fun aTimelineItemEvent(
         groupPosition = groupPosition,
         localSendState = sendState,
         inReplyTo = inReplyTo,
-        debugInfoProvider = { debugInfo },
         isThreaded = isThreaded,
         origin = null,
-        messageShield = messageShield,
+        timelineItemDebugInfoProvider = { debugInfo },
+        messageShieldProvider = { messageShield },
     )
 }
 
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt
index 806a81b7fe7..99f69675fa1 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt
+++ b/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.
     val messageShield = aCriticalShield()
     val items = listOf(
-        (timelineItems.first() as TimelineItem.Event).copy(messageShield = messageShield)
+        (timelineItems.first() as TimelineItem.Event).copy(
+            messageShieldProvider = { messageShield },
+        )
     ) + timelineItems.drop(1)
     CompositionLocalProvider(
         LocalTimelineItemPresenterFactories provides aFakeTimelineItemPresenterFactories(),
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt
index 3fe24fbef04..df0dfb41bf2 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt
+++ b/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"),
                 content = aTimelineItemTextContent().copy(isEdited = true),
             ),
-            aTimelineItemEvent().copy(
+            aTimelineItemEvent(
                 messageShield = MessageShield.AuthenticityNotGuaranteed(isCritical = false),
             ),
-            aTimelineItemEvent().copy(
+            aTimelineItemEvent(
                 messageShield = MessageShield.UnknownDevice(isCritical = true),
             ),
         )
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt
index e71d0cf93e4..21004fb4ab8 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt
+++ b/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,
             inReplyTo = currentTimelineItem.event.inReplyTo()?.map(permalinkParser = permalinkParser),
             isThreaded = currentTimelineItem.event.isThreaded(),
-            debugInfoProvider = currentTimelineItem.event.debugInfoProvider,
             origin = currentTimelineItem.event.origin,
-            messageShield = currentTimelineItem.event.messageShieldProvider.getShield(strict = false)
+            timelineItemDebugInfoProvider = currentTimelineItem.event.timelineItemDebugInfoProvider,
+            messageShieldProvider = currentTimelineItem.event.messageShieldProvider,
         )
     }
 
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt
index 038f5eb9fa1..d13fd992bfc 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt
+++ b/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.UniqueId
 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.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.TimelineItemDebugInfoProvider
 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.ui.messages.reply.InReplyToDetails
@@ -74,9 +76,9 @@ sealed interface TimelineItem {
         val localSendState: LocalEventSendState?,
         val inReplyTo: InReplyToDetails?,
         val isThreaded: Boolean,
-        val debugInfoProvider: EventDebugInfoProvider,
         val origin: TimelineItemEventOrigin?,
-        val messageShield: MessageShield?,
+        val timelineItemDebugInfoProvider: TimelineItemDebugInfoProvider,
+        val messageShieldProvider: MessageShieldProvider,
     ) : TimelineItem {
         val showSenderInformation = groupPosition.isNew() && !isMine
 
@@ -90,7 +92,11 @@ sealed interface TimelineItem {
 
         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
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt
index d6bd4ea301b..f1d8e5f9875 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt
+++ b/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 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(
             mediaSource: MediaSource,
             mimeType: String?,
-            body: String?,
+            filename: String?,
         ): VoiceMessageMediaRepo
     }
 
@@ -61,7 +61,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
     private val matrixMediaLoader: MatrixMediaLoader,
     @Assisted private val mediaSource: MediaSource,
     @Assisted("mimeType") private val mimeType: String?,
-    @Assisted("body") private val body: String?,
+    @Assisted("filename") private val filename: String?,
 ) : VoiceMessageMediaRepo {
     @ContributesBinding(RoomScope::class)
     @AssistedFactory
@@ -69,7 +69,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
         override fun create(
             mediaSource: MediaSource,
             @Assisted("mimeType") mimeType: String?,
-            @Assisted("body") body: String?,
+            @Assisted("filename") filename: String?,
         ): DefaultVoiceMessageMediaRepo
     }
 
@@ -79,7 +79,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
         else -> matrixMediaLoader.downloadMediaFile(
             source = mediaSource,
             mimeType = mimeType,
-            body = body,
+            filename = filename,
         ).mapCatching {
             it.use { mediaFile ->
                 val dest = cachedFile.apply { parentFile?.mkdirs() }
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt
index 42969b9754f..aa339e33655 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt
+++ b/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 mediaSource The media source 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(
             eventId: EventId?,
             mediaSource: MediaSource,
             mimeType: String?,
-            body: String?,
+            filename: String?,
         ): VoiceMessagePlayer
     }
 
@@ -113,7 +113,7 @@ class DefaultVoiceMessagePlayer(
     private val eventId: EventId?,
     mediaSource: MediaSource,
     mimeType: String?,
-    body: String?,
+    filename: String?,
 ) : VoiceMessagePlayer {
     @ContributesBinding(RoomScope::class) // Scoped types can't use @AssistedInject.
     class Factory @Inject constructor(
@@ -124,21 +124,21 @@ class DefaultVoiceMessagePlayer(
             eventId: EventId?,
             mediaSource: MediaSource,
             mimeType: String?,
-            body: String?,
+            filename: String?,
         ): DefaultVoiceMessagePlayer = DefaultVoiceMessagePlayer(
             mediaPlayer = mediaPlayer,
             voiceMessageMediaRepoFactory = voiceMessageMediaRepoFactory,
             eventId = eventId,
             mediaSource = mediaSource,
             mimeType = mimeType,
-            body = body,
+            filename = filename,
         )
     }
 
     private val repo = voiceMessageMediaRepoFactory.create(
         mediaSource = mediaSource,
         mimeType = mimeType,
-        body = body
+        filename = filename,
     )
 
     private var internalState = MutableStateFlow(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt
index 155b47728a1..038de6730da 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt
+++ b/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,
         mediaSource = content.mediaSource,
         mimeType = content.mimeType,
-        body = content.caption,
+        filename = content.filename,
     )
 
     private val play = mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 13733a101d4..8b43d04069b 100644
--- a/gradle/libs.versions.toml
+++ b/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" }
 molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
 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_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
 sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt
index bb3d396202b..b7b44ba45e0 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt
+++ b/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.
      */
-    suspend fun getOidcUrl(): Result<OidcDetails>
+    suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails>
 
     /**
      * Cancel Oidc login sequence.
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt
new file mode 100644
index 00000000000..2444425f3a5
--- /dev/null
+++ b/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
+}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt
index 06f6350b64e..349e651cf1e 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt
+++ b/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 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.
      * @return a [Result] of [MediaFile]
      */
     suspend fun downloadMediaFile(
         source: MediaSource,
         mimeType: String?,
-        body: String?,
+        filename: String?,
         useCache: Boolean = true,
     ): Result<MediaFile>
 }
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt
index 1558ea4af51..b1bc3648d78 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt
+++ b/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 {
     INVITED,
     JOINED,
-    LEFT
+    LEFT,
+    KNOCKED,
 }
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt
index b311b4e6bf7..eb8923d1d6b 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt
+++ b/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 isEditable: Boolean,
     val canBeRepliedTo: Boolean,
-    val isLocal: Boolean,
     val isOwn: Boolean,
     val isRemote: Boolean,
     val localSendState: LocalEventSendState?,
@@ -28,9 +27,9 @@ data class EventTimelineItem(
     val senderProfile: ProfileTimelineDetails,
     val timestamp: Long,
     val content: EventContent,
-    val debugInfoProvider: EventDebugInfoProvider,
     val origin: TimelineItemEventOrigin?,
-    val messageShieldProvider: EventShieldsProvider,
+    val timelineItemDebugInfoProvider: TimelineItemDebugInfoProvider,
+    val messageShieldProvider: MessageShieldProvider,
 ) {
     fun inReplyTo(): InReplyTo? {
         return (content as? MessageContent)?.inReplyTo
@@ -46,10 +45,10 @@ data class EventTimelineItem(
     }
 }
 
-fun interface EventDebugInfoProvider {
-    fun get(): TimelineItemDebugInfo
+fun interface TimelineItemDebugInfoProvider {
+    operator fun invoke(): TimelineItemDebugInfo
 }
 
-fun interface EventShieldsProvider {
-    fun getShield(strict: Boolean): MessageShield?
+fun interface MessageShieldProvider {
+    operator fun invoke(strict: Boolean): MessageShield?
 }
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt
new file mode 100644
index 00000000000..a5b9e4d28f4
--- /dev/null
+++ b/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)
+    }
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
index 47a81e9f650..434b0b2aef2 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
+++ b/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.MatrixHomeServerDetails
 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.qrlogin.MatrixQrCodeLoginData
 import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
@@ -181,11 +182,14 @@ class RustMatrixAuthenticationService @Inject constructor(
 
     private var pendingOidcAuthorizationData: OidcAuthorizationData? = null
 
-    override suspend fun getOidcUrl(): Result<OidcDetails> {
+    override suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails> {
         return withContext(coroutineDispatchers.io) {
             runCatching {
                 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()
                 pendingOidcAuthorizationData = oidcAuthenticationData
                 OidcDetails(url)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt
index eb4d016b6c6..9604d6af6df 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt
+++ b/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(
         source: MediaSource,
         mimeType: String?,
-        body: String?,
+        filename: String?,
         useCache: Boolean,
     ): Result<MediaFile> =
         withContext(mediaDispatcher) {
@@ -71,7 +71,7 @@ class RustMediaLoader(
                 source.toRustMediaSource().use { mediaSource ->
                     val mediaFile = innerClient.getMediaFile(
                         mediaSource = mediaSource,
-                        body = body,
+                        filename = filename,
                         mimeType = mimeType?.takeIf { MimeTypes.hasSubtype(it) } ?: MimeTypes.OctetStream,
                         useCache = useCache,
                         tempDir = cacheDirectory.path,
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt
index c6d60bdca55..607c316b257 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt
+++ b/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.toImmutableList
 import kotlinx.collections.immutable.toPersistentMap
+import org.matrix.rustcomponents.sdk.Membership
 import org.matrix.rustcomponents.sdk.RoomHero
 import org.matrix.rustcomponents.sdk.Membership as RustMembership
 import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo
@@ -65,6 +66,7 @@ fun RustMembership.map(): CurrentUserMembership = when (this) {
     RustMembership.INVITED -> CurrentUserMembership.INVITED
     RustMembership.JOINED -> CurrentUserMembership.JOINED
     RustMembership.LEFT -> CurrentUserMembership.LEFT
+    Membership.KNOCKED -> CurrentUserMembership.KNOCKED
 }
 
 fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) {
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt
index 1291c9e6b89..95b11e10c12 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt
+++ b/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.UserId
 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.EventShieldsProvider
 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.MessageShield
@@ -27,12 +25,10 @@ import kotlinx.collections.immutable.persistentListOf
 import kotlinx.collections.immutable.toImmutableList
 import org.matrix.rustcomponents.sdk.EventOrTransactionId
 import org.matrix.rustcomponents.sdk.EventSendState
-import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfoProvider
 import org.matrix.rustcomponents.sdk.Reaction
 import org.matrix.rustcomponents.sdk.ShieldState
 import uniffi.matrix_sdk_common.ShieldStateCode
 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.EventTimelineItemDebugInfo as RustEventTimelineItemDebugInfo
 import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails
@@ -48,7 +44,6 @@ class EventTimelineItemMapper(
             transactionId = eventOrTransactionId.transactionId(),
             isEditable = isEditable,
             canBeRepliedTo = canBeRepliedTo,
-            isLocal = isLocal,
             isOwn = isOwn,
             isRemote = isRemote,
             localSendState = localSendState?.map(),
@@ -58,9 +53,9 @@ class EventTimelineItemMapper(
             senderProfile = senderProfile.map(),
             timestamp = timestamp.toLong(),
             content = contentMapper.map(content),
-            debugInfoProvider = RustEventDebugInfoProvider(debugInfoProvider),
             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? {
     return (this as? EventOrTransactionId.EventId)?.let { EventId(it.eventId) }
 }
diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt
index 3d51096dd31..3cef900040b 100644
--- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt
+++ b/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(
             source = inputs.mediaSource,
             mimeType = inputs.mediaInfo.mimeType,
-            body = inputs.mediaInfo.filename
+            filename = inputs.mediaInfo.filename
         )
             .onSuccess {
                 mediaFile.value = it
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt
index 1d6e9161240..2fb6186bd4e 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt
@@ -299,7 +299,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
                 .getMediaFile(
                     mediaSource = messageType.source,
                     mimeType = messageType.info?.mimetype,
-                    body = messageType.filename,
+                    filename = messageType.filename,
                 )
             is VideoMessageType -> null // Use the thumbnail here?
             else -> null
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt
index f8818cf341b..c53bbcd347b 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt
+++ b/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 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].
      */
     suspend fun getMediaFile(
         mediaSource: MediaSource,
         mimeType: String?,
-        body: String?,
+        filename: String?,
     ): Result<File>
 }
 
@@ -75,7 +75,7 @@ class DefaultNotificationMediaRepo @AssistedInject constructor(
     override suspend fun getMediaFile(
         mediaSource: MediaSource,
         mimeType: String?,
-        body: String?,
+        filename: String?,
     ): Result<File> {
         val cachedFile = mediaSource.cachedFile()
         return when {
@@ -84,7 +84,7 @@ class DefaultNotificationMediaRepo @AssistedInject constructor(
             else -> matrixMediaLoader.downloadMediaFile(
                 source = mediaSource,
                 mimeType = mimeType,
-                body = body,
+                filename = filename,
             ).mapCatching {
                 it.use { mediaFile ->
                     val dest = cachedFile.apply { parentFile?.mkdirs() }


Test patch:
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt
index 758557923f6..3a4d343203c 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt
+++ b/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()),
     localSendState = sendState,
     inReplyTo = inReplyTo,
-    debugInfoProvider = { debugInfo },
     isThreaded = isThreaded,
     origin = null,
-    messageShield = messageShield,
+    timelineItemDebugInfoProvider = { debugInfo },
+    messageShieldProvider = { messageShield },
 )
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt
index 2e37145ff87..a5a806fe5bb 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt
@@ -41,9 +41,9 @@ class TimelineItemGrouperTest {
         canBeRepliedTo = false,
         inReplyTo = null,
         isThreaded = false,
-        debugInfoProvider = { aTimelineItemDebugInfo() },
         origin = null,
-        messageShield = null,
+        timelineItemDebugInfoProvider = { aTimelineItemDebugInfo() },
+        messageShieldProvider = { null },
     )
     private val aNonGroupableItem = aMessageEvent()
     private val aNonGroupableItemNoEvent = TimelineItem.Virtual(UniqueId("virtual"), aTimelineItemDaySeparatorModel("Today"))
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt
index 462535d3c01..fcf19980973 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt
+++ b/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
     ),
     mimeType = MimeTypes.Ogg,
-    body = "someBody.ogg"
+    filename = "someBody.ogg"
 )
 
 private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg"
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt
index b5a11ba160c..9a82b467768 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt
+++ b/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
     ),
     mimeType = MimeTypes.Ogg,
-    body = "someBody.ogg"
+    filename = "someBody.ogg"
 )
 
 private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg"
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt
index b9b08ab6d44..a980467add9 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt
+++ b/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,
             isEditable = false,
             canBeRepliedTo = false,
-            isLocal = false,
             isOwn = false,
             isRemote = false,
             localSendState = null,
@@ -88,14 +87,14 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf<MatrixTimelineItem>(
             senderProfile = ProfileTimelineDetails.Unavailable,
             timestamp = 9442,
             content = RedactedContent,
-            debugInfoProvider = {
+            origin = null,
+            timelineItemDebugInfoProvider = {
                 TimelineItemDebugInfo(
                     model = "enim",
                     originalJson = null,
-                    latestEditedJson = null
+                    latestEditedJson = null,
                 )
             },
-            origin = null,
             messageShieldProvider = { null },
         ),
     )
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt
index a4770539be3..ceedf0948fb 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt
+++ b/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(),
     content: TimelineItemVoiceContent = aTimelineItemVoiceContent(),
 ) = VoiceMessagePresenter(
-    voiceMessagePlayerFactory = { eventId, mediaSource, mimeType, body ->
+    voiceMessagePlayerFactory = { eventId, mediaSource, mimeType, filename ->
         DefaultVoiceMessagePlayer(
             mediaPlayer = mediaPlayer,
             voiceMessageMediaRepoFactory = { _, _, _ -> voiceMessageMediaRepo },
             eventId = eventId,
             mediaSource = mediaSource,
             mimeType = mimeType,
-            body = body
+            filename = filename
         )
     },
     analyticsService = analyticsService,
diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt
index 1b8c7152ff0..42e710efc23 100644
--- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt
+++ b/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
 
-import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustEventShieldsProvider
-import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustEventTimelineItemDebugInfoProvider
+import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustLazyTimelineItemProvider
 import io.element.android.libraries.matrix.test.AN_EVENT_ID
 import io.element.android.libraries.matrix.test.A_USER_ID
 import org.matrix.rustcomponents.sdk.EventOrTransactionId
@@ -23,7 +22,6 @@ import org.matrix.rustcomponents.sdk.TimelineItemContent
 import uniffi.matrix_sdk_ui.EventItemOrigin
 
 fun aRustEventTimelineItem(
-    isLocal: Boolean = false,
     isRemote: Boolean = true,
     eventOrTransactionId: EventOrTransactionId = EventOrTransactionId.EventId(AN_EVENT_ID.value),
     sender: String = A_USER_ID.value,
@@ -40,7 +38,6 @@ fun aRustEventTimelineItem(
     canBeRepliedTo: Boolean = true,
     shieldsState: ShieldState? = null,
 ) = EventTimelineItem(
-    isLocal = isLocal,
     isRemote = isRemote,
     eventOrTransactionId = eventOrTransactionId,
     sender = sender,
@@ -50,10 +47,12 @@ fun aRustEventTimelineItem(
     isEditable = isEditable,
     canBeRepliedTo = canBeRepliedTo,
     content = content,
-    debugInfoProvider = FakeRustEventTimelineItemDebugInfoProvider(debugInfo),
-    shieldsProvider = FakeRustEventShieldsProvider(shieldsState),
     localSendState = localSendState,
     reactions = reactions,
     readReceipts = readReceipts,
     origin = origin,
+    lazyProvider = FakeRustLazyTimelineItemProvider(
+        debugInfo = debugInfo,
+        shieldsState = shieldsState,
+    )
 )
diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt
deleted file mode 100644
index 93f1e5162b7..00000000000
--- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt
+++ /dev/null
@@ -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
-}
diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventTimelineItemDebugInfoProvider.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustLazyTimelineItemProvider.kt
similarity index 59%
rename from libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventTimelineItemDebugInfoProvider.kt
rename to libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustLazyTimelineItemProvider.kt
index 93ac66318b9..b133ddd0b16 100644
--- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventTimelineItemDebugInfoProvider.kt
+++ b/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 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.ShieldState
 
-class FakeRustEventTimelineItemDebugInfoProvider(
+class FakeRustLazyTimelineItemProvider(
     private val debugInfo: EventTimelineItemDebugInfo = anEventTimelineItemDebugInfo(),
-) : EventTimelineItemDebugInfoProvider(NoPointer) {
-    override fun get(): EventTimelineItemDebugInfo = debugInfo
+    private val shieldsState: ShieldState? = null,
+) : LazyTimelineItemProvider(NoPointer) {
+    override fun getShields(strict: Boolean) = shieldsState
+    override fun debugInfo() = debugInfo
 }
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt
index 8c18629817d..2846542890d 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt
+++ b/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.MatrixHomeServerDetails
 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.qrlogin.MatrixQrCodeLoginData
 import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
@@ -80,7 +81,7 @@ class FakeMatrixAuthenticationService(
         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)
     }
 
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt
index 68bc3ed7646..6a53d8658b4 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt
+++ b/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(
         source: MediaSource,
         mimeType: String?,
-        body: String?,
+        filename: String?,
         useCache: Boolean,
     ): Result<MediaFile> = simulateLongTask {
         if (shouldFail) {
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt
index 85833f183a6..e1c6d9829e1 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt
@@ -42,7 +42,6 @@ fun anEventTimelineItem(
     transactionId: TransactionId? = null,
     isEditable: Boolean = false,
     canBeRepliedTo: Boolean = false,
-    isLocal: Boolean = false,
     isOwn: Boolean = false,
     isRemote: Boolean = false,
     localSendState: LocalEventSendState? = null,
@@ -59,7 +58,6 @@ fun anEventTimelineItem(
     transactionId = transactionId,
     isEditable = isEditable,
     canBeRepliedTo = canBeRepliedTo,
-    isLocal = isLocal,
     isOwn = isOwn,
     isRemote = isRemote,
     localSendState = localSendState,
@@ -69,8 +67,8 @@ fun anEventTimelineItem(
     senderProfile = senderProfile,
     timestamp = timestamp,
     content = content,
-    debugInfoProvider = { debugInfo },
     origin = null,
+    timelineItemDebugInfoProvider = { debugInfo },
     messageShieldProvider = { messageShield },
 )
 
diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt
index 8ab7bf7d9c4..68ce164284c 100644
--- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt
+++ b/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(
         mediaSource: MediaSource,
         mimeType: String?,
-        body: String?,
+        filename: String?,
     ): Result<File> {
         return Result.failure(IllegalStateException("Fake class"))
     }