Merge pull request #3694 from element-hq/feature/bma/oidcPrompt
OIDC prompt
This commit is contained in:
commit
80a052c7cf
37 changed files with 202 additions and 142 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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() }
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 },
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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 },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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" }
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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?
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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 },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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() }
|
||||||
|
|
|
||||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue