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

OIDC prompt
This commit is contained in:
Benoit Marty 2024-10-17 09:16:02 +02:00 committed by GitHub
commit 80a052c7cf
37 changed files with 202 additions and 142 deletions

View file

@ -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)

View file

@ -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,

View file

@ -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 },
)
}

View file

@ -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(),

View file

@ -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),
),
)

View file

@ -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,
)
}

View file

@ -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

View file

@ -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() }

View file

@ -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(

View file

@ -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)

View file

@ -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 },
)

View file

@ -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"))

View file

@ -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"

View file

@ -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"

View file

@ -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 },
),
)

View file

@ -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,