fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.6.18 (#4894)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
renovate[bot] 2025-06-18 15:10:47 +00:00 committed by GitHub
parent b2e17622ed
commit 31bb1848e4
23 changed files with 193 additions and 89 deletions

View file

@ -26,6 +26,10 @@ import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
import io.element.android.libraries.matrix.api.room.message.replyInThread
import io.element.android.libraries.matrix.ui.messages.reply.eventId
import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -98,6 +102,18 @@ class SendLocationPresenter @Inject constructor(
event: SendLocationEvents.SendLocation, event: SendLocationEvents.SendLocation,
mode: SendLocationState.Mode, mode: SendLocationState.Mode,
) { ) {
val replyMode = messageComposerContext.composerMode as? MessageComposerMode.Reply
val replyParams = replyMode?.replyToDetails?.let { details ->
if (replyMode.inThread) {
replyInThread(details.eventId())
} else {
ReplyParameters(
inReplyToEventId = details.eventId(),
enforceThreadReply = false,
replyWithinThread = false
)
}
}
when (mode) { when (mode) {
SendLocationState.Mode.PinLocation -> { SendLocationState.Mode.PinLocation -> {
val geoUri = event.cameraPosition.toGeoUri() val geoUri = event.cameraPosition.toGeoUri()
@ -106,7 +122,8 @@ class SendLocationPresenter @Inject constructor(
geoUri = geoUri, geoUri = geoUri,
description = null, description = null,
zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(), zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(),
assetType = AssetType.PIN assetType = AssetType.PIN,
replyParameters = replyParams,
) )
analyticsService.capture( analyticsService.capture(
Composer( Composer(
@ -124,7 +141,8 @@ class SendLocationPresenter @Inject constructor(
geoUri = geoUri, geoUri = geoUri,
description = null, description = null,
zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(), zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(),
assetType = AssetType.SENDER assetType = AssetType.SENDER,
replyParameters = replyParams,
) )
analyticsService.capture( analyticsService.capture(
Composer( Composer(

View file

@ -22,6 +22,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsS
import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId
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.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
@ -263,7 +264,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `share sender location`() = runTest { fun `share sender location`() = runTest {
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ -> val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, ReplyParameters?, Result<Unit>> { _, _, _, _, _, _ ->
Result.success(Unit) Result.success(Unit)
} }
val joinedRoom = FakeJoinedRoom( val joinedRoom = FakeJoinedRoom(
@ -310,6 +311,7 @@ class SendLocationPresenterTest {
value(null), value(null),
value(15), value(15),
value(AssetType.SENDER), value(AssetType.SENDER),
value(null),
) )
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
@ -326,7 +328,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `share pin location`() = runTest { fun `share pin location`() = runTest {
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ -> val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, ReplyParameters?, Result<Unit>> { _, _, _, _, _, _ ->
Result.success(Unit) Result.success(Unit)
} }
val joinedRoom = FakeJoinedRoom( val joinedRoom = FakeJoinedRoom(
@ -373,6 +375,7 @@ class SendLocationPresenterTest {
value(null), value(null),
value(15), value(15),
value(AssetType.PIN), value(AssetType.PIN),
value(null),
) )
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
@ -389,7 +392,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `composer context passes through analytics`() = runTest { fun `composer context passes through analytics`() = runTest {
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ -> val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, ReplyParameters?, Result<Unit>> { _, _, _, _, _, _ ->
Result.success(Unit) Result.success(Unit)
} }
val joinedRoom = FakeJoinedRoom( val joinedRoom = FakeJoinedRoom(

View file

@ -8,9 +8,10 @@
package io.element.android.features.messages.impl.draft package io.element.android.features.messages.impl.draft
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
interface ComposerDraftService { interface ComposerDraftService {
suspend fun loadDraft(roomId: RoomId, isVolatile: Boolean): ComposerDraft? suspend fun loadDraft(roomId: RoomId, threadRoot: ThreadId?, isVolatile: Boolean): ComposerDraft?
suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?, isVolatile: Boolean) suspend fun updateDraft(roomId: RoomId, threadRoot: ThreadId?, draft: ComposerDraft?, isVolatile: Boolean)
} }

View file

@ -8,9 +8,10 @@
package io.element.android.features.messages.impl.draft package io.element.android.features.messages.impl.draft
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
interface ComposerDraftStore { interface ComposerDraftStore {
suspend fun loadDraft(roomId: RoomId): ComposerDraft? suspend fun loadDraft(roomId: RoomId, threadRoot: ThreadId?): ComposerDraft?
suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?) suspend fun updateDraft(roomId: RoomId, threadRoot: ThreadId?, draft: ComposerDraft?)
} }

View file

@ -10,6 +10,7 @@ package io.element.android.features.messages.impl.draft
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
import javax.inject.Inject import javax.inject.Inject
@ -18,12 +19,12 @@ class DefaultComposerDraftService @Inject constructor(
private val volatileComposerDraftStore: VolatileComposerDraftStore, private val volatileComposerDraftStore: VolatileComposerDraftStore,
private val matrixComposerDraftStore: MatrixComposerDraftStore, private val matrixComposerDraftStore: MatrixComposerDraftStore,
) : ComposerDraftService { ) : ComposerDraftService {
override suspend fun loadDraft(roomId: RoomId, isVolatile: Boolean): ComposerDraft? { override suspend fun loadDraft(roomId: RoomId, threadRoot: ThreadId?, isVolatile: Boolean): ComposerDraft? {
return getStore(isVolatile).loadDraft(roomId) return getStore(isVolatile).loadDraft(roomId, threadRoot)
} }
override suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?, isVolatile: Boolean) { override suspend fun updateDraft(roomId: RoomId, threadRoot: ThreadId?, draft: ComposerDraft?, isVolatile: Boolean) {
getStore(isVolatile).updateDraft(roomId, draft) getStore(isVolatile).updateDraft(roomId, threadRoot, draft)
} }
private fun getStore(isVolatile: Boolean): ComposerDraftStore { private fun getStore(isVolatile: Boolean): ComposerDraftStore {

View file

@ -9,6 +9,7 @@ package io.element.android.features.messages.impl.draft
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -20,26 +21,26 @@ import javax.inject.Inject
class MatrixComposerDraftStore @Inject constructor( class MatrixComposerDraftStore @Inject constructor(
private val client: MatrixClient, private val client: MatrixClient,
) : ComposerDraftStore { ) : ComposerDraftStore {
override suspend fun loadDraft(roomId: RoomId): ComposerDraft? { override suspend fun loadDraft(roomId: RoomId, threadRoot: ThreadId?): ComposerDraft? {
return client.getRoom(roomId)?.use { room -> return client.getRoom(roomId)?.use { room ->
room.loadComposerDraft() room.loadComposerDraft(threadRoot)
.onFailure { .onFailure {
Timber.e(it, "Failed to load composer draft for room $roomId") Timber.e(it, "Failed to load composer draft for room $roomId")
} }
.onSuccess { draft -> .onSuccess { draft ->
room.clearComposerDraft() room.clearComposerDraft(threadRoot)
Timber.d("Loaded composer draft for room $roomId : $draft") Timber.d("Loaded composer draft for room $roomId : $draft")
} }
.getOrNull() .getOrNull()
} }
} }
override suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?) { override suspend fun updateDraft(roomId: RoomId, threadRoot: ThreadId?, draft: ComposerDraft?) {
client.getRoom(roomId)?.use { room -> client.getRoom(roomId)?.use { room ->
val updateDraftResult = if (draft == null) { val updateDraftResult = if (draft == null) {
room.clearComposerDraft() room.clearComposerDraft(threadRoot)
} else { } else {
room.saveComposerDraft(draft) room.saveComposerDraft(draft, threadRoot)
} }
updateDraftResult updateDraftResult
.onFailure { .onFailure {

View file

@ -8,6 +8,7 @@
package io.element.android.features.messages.impl.draft package io.element.android.features.messages.impl.draft
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
import javax.inject.Inject import javax.inject.Inject
@ -17,18 +18,20 @@ import javax.inject.Inject
* Currently it's used to store draft message when moving to edit mode. * Currently it's used to store draft message when moving to edit mode.
*/ */
class VolatileComposerDraftStore @Inject constructor() : ComposerDraftStore { class VolatileComposerDraftStore @Inject constructor() : ComposerDraftStore {
private val drafts: MutableMap<RoomId, ComposerDraft> = mutableMapOf() private val drafts: MutableMap<String, ComposerDraft> = mutableMapOf()
override suspend fun loadDraft(roomId: RoomId): ComposerDraft? { override suspend fun loadDraft(roomId: RoomId, threadRoot: ThreadId?): ComposerDraft? {
val key = threadRoot?.value ?: roomId.value
// Remove the draft from the map when it is loaded // Remove the draft from the map when it is loaded
return drafts.remove(roomId) return drafts.remove(key)
} }
override suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?) { override suspend fun updateDraft(roomId: RoomId, threadRoot: ThreadId?, draft: ComposerDraft?) {
val key = threadRoot?.value ?: roomId.value
if (draft == null) { if (draft == null) {
drafts.remove(roomId) drafts.remove(key)
} else { } else {
drafts[roomId] = draft drafts[key] = draft
} }
} }
} }

View file

@ -219,7 +219,12 @@ class MessageComposerPresenter @AssistedInject constructor(
) )
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
val draft = draftService.loadDraft(room.roomId, isVolatile = false) val draft = draftService.loadDraft(
roomId = room.roomId,
// TODO support threads in composer
threadRoot = null,
isVolatile = false
)
if (draft != null) { if (draft != null) {
applyDraft(draft, markdownTextEditorState, richTextEditorState) applyDraft(draft, markdownTextEditorState, richTextEditorState)
} }
@ -539,7 +544,9 @@ class MessageComposerPresenter @AssistedInject constructor(
draftService.updateDraft( draftService.updateDraft(
roomId = room.roomId, roomId = room.roomId,
draft = draft, draft = draft,
isVolatile = isVolatile isVolatile = isVolatile,
// TODO support threads in composer
threadRoot = null,
) )
} }
@ -700,7 +707,12 @@ class MessageComposerPresenter @AssistedInject constructor(
fromEdit: Boolean, fromEdit: Boolean,
) { ) {
// Use the volatile draft only when coming from edit mode otherwise. // Use the volatile draft only when coming from edit mode otherwise.
val draft = draftService.loadDraft(room.roomId, isVolatile = true).takeIf { fromEdit } val draft = draftService.loadDraft(
roomId = room.roomId,
// TODO support threads in composer
threadRoot = null,
isVolatile = true
).takeIf { fromEdit }
if (draft != null) { if (draft != null) {
applyDraft(draft, markdownTextEditorState, richTextEditorState) applyDraft(draft, markdownTextEditorState, richTextEditorState)
} else { } else {

View file

@ -80,6 +80,7 @@ internal fun MessageShield.toText(): String {
is MessageShield.UnverifiedIdentity -> CommonStrings.event_shield_reason_unverified_identity is MessageShield.UnverifiedIdentity -> CommonStrings.event_shield_reason_unverified_identity
is MessageShield.SentInClear -> CommonStrings.event_shield_reason_sent_in_clear is MessageShield.SentInClear -> CommonStrings.event_shield_reason_sent_in_clear
is MessageShield.VerificationViolation -> CommonStrings.event_shield_reason_previously_verified is MessageShield.VerificationViolation -> CommonStrings.event_shield_reason_previously_verified
is MessageShield.MismatchedSender -> CommonStrings.event_shield_mismatched_sender
} }
) )
} }
@ -91,7 +92,8 @@ internal fun MessageShield.toIcon(): ImageVector {
is MessageShield.UnknownDevice, is MessageShield.UnknownDevice,
is MessageShield.UnsignedDevice, is MessageShield.UnsignedDevice,
is MessageShield.UnverifiedIdentity, is MessageShield.UnverifiedIdentity,
is MessageShield.VerificationViolation -> CompoundIcons.HelpSolid() is MessageShield.VerificationViolation,
is MessageShield.MismatchedSender -> CompoundIcons.HelpSolid()
is MessageShield.SentInClear -> CompoundIcons.LockOff() is MessageShield.SentInClear -> CompoundIcons.LockOff()
} }
} }
@ -122,6 +124,9 @@ internal fun MessageShieldViewPreview() {
MessageShieldView( MessageShieldView(
shield = MessageShield.VerificationViolation(false) shield = MessageShield.VerificationViolation(false)
) )
MessageShieldView(
shield = MessageShield.MismatchedSender(false)
)
} }
} }
} }

View file

@ -8,12 +8,22 @@
package io.element.android.features.messages.impl.draft package io.element.android.features.messages.impl.draft
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
class FakeComposerDraftService : ComposerDraftService { class FakeComposerDraftService : ComposerDraftService {
var loadDraftLambda: (RoomId, Boolean) -> ComposerDraft? = { _, _ -> null } var loadDraftLambda: (RoomId, ThreadId?, Boolean) -> ComposerDraft? = { _, _, _ -> null }
override suspend fun loadDraft(roomId: RoomId, isVolatile: Boolean): ComposerDraft? = loadDraftLambda(roomId, isVolatile) override suspend fun loadDraft(
roomId: RoomId,
threadRoot: ThreadId?,
isVolatile: Boolean
): ComposerDraft? = loadDraftLambda(roomId, threadRoot, isVolatile)
var saveDraftLambda: (RoomId, ComposerDraft?, Boolean) -> Unit = { _, _, _ -> } var saveDraftLambda: (RoomId, ThreadId?, ComposerDraft?, Boolean) -> Unit = { _, _, _, _ -> }
override suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?, isVolatile: Boolean) = saveDraftLambda(roomId, draft, isVolatile) override suspend fun updateDraft(
roomId: RoomId,
threadRoot: ThreadId?,
draft: ComposerDraft?,
isVolatile: Boolean
) = saveDraftLambda(roomId, threadRoot, draft, isVolatile)
} }

View file

@ -11,6 +11,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THREAD_ID
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@ -21,27 +22,51 @@ class VolatileComposerDraftStoreTest {
@Test @Test
fun `when storing a non-null draft and then loading it, it's loaded and removed`() = runTest { fun `when storing a non-null draft and then loading it, it's loaded and removed`() = runTest {
val initialDraft = sut.loadDraft(roomId) val initialDraft = sut.loadDraft(roomId = roomId, threadRoot = null)
assertThat(initialDraft).isNull() assertThat(initialDraft).isNull()
sut.updateDraft(roomId, draft) sut.updateDraft(roomId = roomId, threadRoot = null, draft = draft)
val loadedDraft = sut.loadDraft(roomId) val loadedDraft = sut.loadDraft(roomId = roomId, threadRoot = null)
assertThat(loadedDraft).isEqualTo(draft) assertThat(loadedDraft).isEqualTo(draft)
val loadedDraftAfter = sut.loadDraft(roomId) val loadedDraftAfter = sut.loadDraft(roomId = roomId, threadRoot = null)
assertThat(loadedDraftAfter).isNull() assertThat(loadedDraftAfter).isNull()
// In thread:
val threadRoot = A_THREAD_ID
val initialThreadDraft = sut.loadDraft(roomId = roomId, threadRoot = threadRoot)
assertThat(initialThreadDraft).isNull()
sut.updateDraft(roomId = roomId, threadRoot = threadRoot, draft = draft)
val loadedThreadDraft = sut.loadDraft(roomId = roomId, threadRoot = threadRoot)
assertThat(loadedThreadDraft).isEqualTo(draft)
val loadedThreadDraftAfter = sut.loadDraft(roomId = roomId, threadRoot = threadRoot)
assertThat(loadedThreadDraftAfter).isNull()
} }
@Test @Test
fun `when storing a null draft and then loading it, it's removing the previous one`() = runTest { fun `when storing a null draft and then loading it, it's removing the previous one`() = runTest {
val initialDraft = sut.loadDraft(roomId) val initialDraft = sut.loadDraft(roomId = roomId, threadRoot = null)
assertThat(initialDraft).isNull() assertThat(initialDraft).isNull()
sut.updateDraft(roomId, draft) sut.updateDraft(roomId = roomId, threadRoot = null, draft = draft)
sut.updateDraft(roomId, null) sut.updateDraft(roomId = roomId, threadRoot = null, draft = null)
val loadedDraft = sut.loadDraft(roomId) val loadedDraft = sut.loadDraft(roomId = roomId, threadRoot = null)
assertThat(loadedDraft).isNull() assertThat(loadedDraft).isNull()
// In thread:
val threadRoot = A_THREAD_ID
val initialThreadDraft = sut.loadDraft(roomId = roomId, threadRoot = threadRoot)
assertThat(initialThreadDraft).isNull()
sut.updateDraft(roomId = roomId, threadRoot = threadRoot, draft = draft)
sut.updateDraft(roomId = roomId, threadRoot = threadRoot, draft = null)
val loadedThreadDraft = sut.loadDraft(roomId = roomId, threadRoot = threadRoot)
assertThat(loadedThreadDraft).isNull()
} }
} }

View file

@ -37,6 +37,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
@ -178,10 +179,10 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - change mode to edit`() = runTest { fun `present - change mode to edit`() = runTest {
val loadDraftLambda = lambdaRecorder { _: RoomId, _: Boolean -> val loadDraftLambda = lambdaRecorder { _: RoomId, _: ThreadId?, _: Boolean ->
ComposerDraft(A_MESSAGE, A_MESSAGE, ComposerDraftType.NewMessage) ComposerDraft(A_MESSAGE, A_MESSAGE, ComposerDraftType.NewMessage)
} }
val updateDraftLambda = lambdaRecorder { _: RoomId, _: ComposerDraft?, _: Boolean -> } val updateDraftLambda = lambdaRecorder { _: RoomId, _: ThreadId?, _: ComposerDraft?, _: Boolean -> }
val draftService = FakeComposerDraftService().apply { val draftService = FakeComposerDraftService().apply {
this.loadDraftLambda = loadDraftLambda this.loadDraftLambda = loadDraftLambda
this.saveDraftLambda = updateDraftLambda this.saveDraftLambda = updateDraftLambda
@ -207,23 +208,23 @@ class MessageComposerPresenterTest {
.isCalledExactly(2) .isCalledExactly(2)
.withSequence( .withSequence(
// Automatic load of draft // Automatic load of draft
listOf(value(A_ROOM_ID), value(false)), listOf(value(A_ROOM_ID), value(null), value(false)),
// Load of volatile draft when closing edit mode // Load of volatile draft when closing edit mode
listOf(value(A_ROOM_ID), value(true)) listOf(value(A_ROOM_ID), value(null), value(true))
) )
assert(updateDraftLambda) assert(updateDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), any(), value(true)) .with(value(A_ROOM_ID), value(null), any(), value(true))
} }
} }
@Test @Test
fun `present - change mode to edit caption`() = runTest { fun `present - change mode to edit caption`() = runTest {
val loadDraftLambda = lambdaRecorder { _: RoomId, _: Boolean -> val loadDraftLambda = lambdaRecorder { _: RoomId, _: ThreadId?, _: Boolean ->
ComposerDraft(A_MESSAGE, A_MESSAGE, ComposerDraftType.NewMessage) ComposerDraft(A_MESSAGE, A_MESSAGE, ComposerDraftType.NewMessage)
} }
val updateDraftLambda = lambdaRecorder { _: RoomId, _: ComposerDraft?, _: Boolean -> } val updateDraftLambda = lambdaRecorder { _: RoomId, _: ThreadId?, _: ComposerDraft?, _: Boolean -> }
val draftService = FakeComposerDraftService().apply { val draftService = FakeComposerDraftService().apply {
this.loadDraftLambda = loadDraftLambda this.loadDraftLambda = loadDraftLambda
this.saveDraftLambda = updateDraftLambda this.saveDraftLambda = updateDraftLambda
@ -249,13 +250,13 @@ class MessageComposerPresenterTest {
.isCalledExactly(2) .isCalledExactly(2)
.withSequence( .withSequence(
// Automatic load of draft // Automatic load of draft
listOf(value(A_ROOM_ID), value(false)), listOf(value(A_ROOM_ID), value(null), value(false)),
// Load of volatile draft when closing edit mode // Load of volatile draft when closing edit mode
listOf(value(A_ROOM_ID), value(true)) listOf(value(A_ROOM_ID), value(null), value(true))
) )
assert(updateDraftLambda) assert(updateDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), any(), value(true)) .with(value(A_ROOM_ID), value(null), any(), value(true))
} }
} }
@ -303,10 +304,10 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - change mode to reply after edit`() = runTest { fun `present - change mode to reply after edit`() = runTest {
val loadDraftLambda = lambdaRecorder { _: RoomId, _: Boolean -> val loadDraftLambda = lambdaRecorder { _: RoomId, _: ThreadId?, _: Boolean ->
ComposerDraft(A_MESSAGE, A_MESSAGE, ComposerDraftType.NewMessage) ComposerDraft(A_MESSAGE, A_MESSAGE, ComposerDraftType.NewMessage)
} }
val updateDraftLambda = lambdaRecorder { _: RoomId, _: ComposerDraft?, _: Boolean -> } val updateDraftLambda = lambdaRecorder { _: RoomId, _: ThreadId?, _: ComposerDraft?, _: Boolean -> }
val draftService = FakeComposerDraftService().apply { val draftService = FakeComposerDraftService().apply {
this.loadDraftLambda = loadDraftLambda this.loadDraftLambda = loadDraftLambda
this.saveDraftLambda = updateDraftLambda this.saveDraftLambda = updateDraftLambda
@ -333,11 +334,11 @@ class MessageComposerPresenterTest {
assert(loadDraftLambda) assert(loadDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), value(false)) .with(value(A_ROOM_ID), value(null), value(false))
assert(updateDraftLambda) assert(updateDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), any(), value(true)) .with(value(A_ROOM_ID), value(null), any(), value(true))
} }
} }
@ -1246,7 +1247,7 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - when there is no draft, nothing is restored`() = runTest { fun `present - when there is no draft, nothing is restored`() = runTest {
val loadDraftLambda = lambdaRecorder<RoomId, Boolean, ComposerDraft?> { _, _ -> null } val loadDraftLambda = lambdaRecorder<RoomId, ThreadId?, Boolean, ComposerDraft?> { _, _, _ -> null }
val composerDraftService = FakeComposerDraftService().apply { val composerDraftService = FakeComposerDraftService().apply {
this.loadDraftLambda = loadDraftLambda this.loadDraftLambda = loadDraftLambda
} }
@ -1257,7 +1258,7 @@ class MessageComposerPresenterTest {
awaitFirstItem() awaitFirstItem()
assert(loadDraftLambda) assert(loadDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), value(false)) .with(value(A_ROOM_ID), value(null), value(false))
ensureAllEventsConsumed() ensureAllEventsConsumed()
} }
@ -1265,7 +1266,7 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - when there is a draft for new message with plain text, it is restored`() = runTest { fun `present - when there is a draft for new message with plain text, it is restored`() = runTest {
val loadDraftLambda = lambdaRecorder<RoomId, Boolean, ComposerDraft?> { _, _ -> val loadDraftLambda = lambdaRecorder<RoomId, ThreadId?, Boolean, ComposerDraft?> { _, _, _ ->
ComposerDraft(plainText = A_MESSAGE, htmlText = null, draftType = ComposerDraftType.NewMessage) ComposerDraft(plainText = A_MESSAGE, htmlText = null, draftType = ComposerDraftType.NewMessage)
} }
val composerDraftService = FakeComposerDraftService().apply { val composerDraftService = FakeComposerDraftService().apply {
@ -1286,7 +1287,7 @@ class MessageComposerPresenterTest {
} }
assert(loadDraftLambda) assert(loadDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), value(false)) .with(value(A_ROOM_ID), value(null), value(false))
ensureAllEventsConsumed() ensureAllEventsConsumed()
} }
@ -1294,7 +1295,7 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - when there is a draft for new message with rich text, it is restored`() = runTest { fun `present - when there is a draft for new message with rich text, it is restored`() = runTest {
val loadDraftLambda = lambdaRecorder<RoomId, Boolean, ComposerDraft?> { _, _ -> val loadDraftLambda = lambdaRecorder<RoomId, ThreadId?, Boolean, ComposerDraft?> { _, _, _ ->
ComposerDraft( ComposerDraft(
plainText = A_MESSAGE, plainText = A_MESSAGE,
htmlText = A_MESSAGE, htmlText = A_MESSAGE,
@ -1320,14 +1321,14 @@ class MessageComposerPresenterTest {
} }
assert(loadDraftLambda) assert(loadDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), value(false)) .with(value(A_ROOM_ID), value(null), value(false))
ensureAllEventsConsumed() ensureAllEventsConsumed()
} }
} }
@Test @Test
fun `present - when there is a draft for edit, it is restored`() = runTest { fun `present - when there is a draft for edit, it is restored`() = runTest {
val loadDraftLambda = lambdaRecorder<RoomId, Boolean, ComposerDraft?> { _, _ -> val loadDraftLambda = lambdaRecorder<RoomId, ThreadId?, Boolean, ComposerDraft?> { _, _, _ ->
ComposerDraft( ComposerDraft(
plainText = A_MESSAGE, plainText = A_MESSAGE,
htmlText = null, htmlText = null,
@ -1354,7 +1355,7 @@ class MessageComposerPresenterTest {
} }
assert(loadDraftLambda) assert(loadDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), value(false)) .with(value(A_ROOM_ID), value(null), value(false))
ensureAllEventsConsumed() ensureAllEventsConsumed()
} }
@ -1362,7 +1363,7 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - when there is a draft for reply, it is restored`() = runTest { fun `present - when there is a draft for reply, it is restored`() = runTest {
val loadDraftLambda = lambdaRecorder<RoomId, Boolean, ComposerDraft?> { _, _ -> val loadDraftLambda = lambdaRecorder<RoomId, ThreadId?, Boolean, ComposerDraft?> { _, _, _ ->
ComposerDraft( ComposerDraft(
plainText = A_MESSAGE, plainText = A_MESSAGE,
htmlText = null, htmlText = null,
@ -1400,7 +1401,7 @@ class MessageComposerPresenterTest {
} }
assert(loadDraftLambda) assert(loadDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), value(false)) .with(value(A_ROOM_ID), value(null), value(false))
assert(loadReplyDetailsLambda) assert(loadReplyDetailsLambda)
.isCalledOnce() .isCalledOnce()
@ -1412,7 +1413,7 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - when save draft event is invoked and composer is empty then service is called with null draft`() = runTest { fun `present - when save draft event is invoked and composer is empty then service is called with null draft`() = runTest {
val saveDraftLambda = lambdaRecorder<RoomId, ComposerDraft?, Boolean, Unit> { _, _, _ -> } val saveDraftLambda = lambdaRecorder<RoomId, ThreadId?, ComposerDraft?, Boolean, Unit> { _, _, _, _ -> }
val composerDraftService = FakeComposerDraftService().apply { val composerDraftService = FakeComposerDraftService().apply {
this.saveDraftLambda = saveDraftLambda this.saveDraftLambda = saveDraftLambda
} }
@ -1425,13 +1426,13 @@ class MessageComposerPresenterTest {
advanceUntilIdle() advanceUntilIdle()
assert(saveDraftLambda) assert(saveDraftLambda)
.isCalledOnce() .isCalledOnce()
.with(value(A_ROOM_ID), value(null), value(false)) .with(value(A_ROOM_ID), value(null), value(null), value(false))
} }
} }
@Test @Test
fun `present - when save draft event is invoked and composer is not empty then service is called`() = runTest { fun `present - when save draft event is invoked and composer is not empty then service is called`() = runTest {
val saveDraftLambda = lambdaRecorder<RoomId, ComposerDraft?, Boolean, Unit> { _, _, _ -> } val saveDraftLambda = lambdaRecorder<RoomId, ThreadId?, ComposerDraft?, Boolean, Unit> { _, _, _, _ -> }
val composerDraftService = FakeComposerDraftService().apply { val composerDraftService = FakeComposerDraftService().apply {
this.saveDraftLambda = saveDraftLambda this.saveDraftLambda = saveDraftLambda
} }
@ -1478,27 +1479,32 @@ class MessageComposerPresenterTest {
.withSequence( .withSequence(
listOf( listOf(
value(A_ROOM_ID), value(A_ROOM_ID),
value(null),
value(ComposerDraft(plainText = A_MESSAGE, htmlText = null, draftType = ComposerDraftType.NewMessage)), value(ComposerDraft(plainText = A_MESSAGE, htmlText = null, draftType = ComposerDraftType.NewMessage)),
value(false) value(false)
), ),
listOf( listOf(
value(A_ROOM_ID), value(A_ROOM_ID),
value(null),
value(ComposerDraft(plainText = A_MESSAGE, htmlText = A_MESSAGE, draftType = ComposerDraftType.NewMessage)), value(ComposerDraft(plainText = A_MESSAGE, htmlText = A_MESSAGE, draftType = ComposerDraftType.NewMessage)),
value(false) value(false)
), ),
listOf( listOf(
value(A_ROOM_ID), value(A_ROOM_ID),
value(null),
value(ComposerDraft(plainText = A_MESSAGE, htmlText = A_MESSAGE, draftType = ComposerDraftType.NewMessage)), value(ComposerDraft(plainText = A_MESSAGE, htmlText = A_MESSAGE, draftType = ComposerDraftType.NewMessage)),
// The volatile draft created when switching to edit mode. // The volatile draft created when switching to edit mode.
value(true) value(true)
), ),
listOf( listOf(
value(A_ROOM_ID), value(A_ROOM_ID),
value(null),
value(ComposerDraft(plainText = A_MESSAGE, htmlText = A_MESSAGE, draftType = ComposerDraftType.Edit(AN_EVENT_ID))), value(ComposerDraft(plainText = A_MESSAGE, htmlText = A_MESSAGE, draftType = ComposerDraftType.Edit(AN_EVENT_ID))),
value(false) value(false)
), ),
listOf( listOf(
value(A_ROOM_ID), value(A_ROOM_ID),
value(null),
// When moving from edit mode, text composer is cleared, so the draft is null // When moving from edit mode, text composer is cleared, so the draft is null
value(null), value(null),
value(false) value(false)

View file

@ -172,7 +172,7 @@ jsoup = "org.jsoup:jsoup:1.20.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.1.0" molecule-runtime = "app.cash.molecule:molecule-runtime:2.1.0"
timber = "com.jakewharton.timber:timber:5.0.1" timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.6.10" matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.6.18"
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" }

View file

@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.api.room
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
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.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
@ -219,17 +220,17 @@ interface BaseRoom : Closeable {
/** /**
* Store the given `ComposerDraft` in the state store of this room. * Store the given `ComposerDraft` in the state store of this room.
*/ */
suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> suspend fun saveComposerDraft(composerDraft: ComposerDraft, threadRoot: ThreadId?): Result<Unit>
/** /**
* Retrieve the `ComposerDraft` stored in the state store for this room. * Retrieve the `ComposerDraft` stored in the state store for this room.
*/ */
suspend fun loadComposerDraft(): Result<ComposerDraft?> suspend fun loadComposerDraft(threadRoot: ThreadId?): Result<ComposerDraft?>
/** /**
* Clear the `ComposerDraft` stored in the state store for this room. * Clear the `ComposerDraft` stored in the state store for this room.
*/ */
suspend fun clearComposerDraft(): Result<Unit> suspend fun clearComposerDraft(threadRoot: ThreadId?): Result<Unit>
/** /**
* Reports a room as inappropriate to the server. * Reports a room as inappropriate to the server.

View file

@ -131,6 +131,7 @@ interface Timeline : AutoCloseable {
* @param zoomLevel Optional zoom level to display the map at. * @param zoomLevel Optional zoom level to display the map at.
* @param assetType Optional type of the location asset. * @param assetType Optional type of the location asset.
* Set to SENDER if sharing own location. Set to PIN if sharing any location. * Set to SENDER if sharing own location. Set to PIN if sharing any location.
* @param replyParameters Optional reply parameters to use when sending the location.
*/ */
suspend fun sendLocation( suspend fun sendLocation(
body: String, body: String,
@ -138,6 +139,7 @@ interface Timeline : AutoCloseable {
description: String? = null, description: String? = null,
zoomLevel: Int? = null, zoomLevel: Int? = null,
assetType: AssetType? = null, assetType: AssetType? = null,
replyParameters: ReplyParameters?,
): Result<Unit> ): Result<Unit>
suspend fun sendVoiceMessage( suspend fun sendVoiceMessage(

View file

@ -28,6 +28,9 @@ sealed interface MessageShield {
/** The sender was previously verified but is not anymore. */ /** The sender was previously verified but is not anymore. */
data class VerificationViolation(val isCritical: Boolean) : MessageShield data class VerificationViolation(val isCritical: Boolean) : MessageShield
/** The sender of the event does not match the owner of the device that created the Megolm session. */
data class MismatchedSender(val isCritical: Boolean) : MessageShield
} }
val MessageShield.isCritical: Boolean val MessageShield.isCritical: Boolean
@ -38,4 +41,5 @@ val MessageShield.isCritical: Boolean
is MessageShield.UnverifiedIdentity -> isCritical is MessageShield.UnverifiedIdentity -> isCritical
is MessageShield.SentInClear -> isCritical is MessageShield.SentInClear -> isCritical
is MessageShield.VerificationViolation -> isCritical is MessageShield.VerificationViolation -> isCritical
is MessageShield.MismatchedSender -> isCritical
} }

View file

@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.api.core.DeviceId
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
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.room.BaseRoom import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.MessageEventType
@ -266,24 +267,24 @@ class RustBaseRoom(
} }
} }
override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = withContext(roomDispatcher) { override suspend fun saveComposerDraft(composerDraft: ComposerDraft, threadRoot: ThreadId?): Result<Unit> = withContext(roomDispatcher) {
runCatchingExceptions { runCatchingExceptions {
Timber.d("saveComposerDraft: $composerDraft into $roomId") Timber.d("saveComposerDraft: $composerDraft into $roomId for thread root: $threadRoot")
innerRoom.saveComposerDraft(composerDraft.into()) innerRoom.saveComposerDraft(composerDraft.into(), threadRoot = threadRoot?.value)
} }
} }
override suspend fun loadComposerDraft(): Result<ComposerDraft?> = withContext(roomDispatcher) { override suspend fun loadComposerDraft(threadRoot: ThreadId?): Result<ComposerDraft?> = withContext(roomDispatcher) {
runCatchingExceptions { runCatchingExceptions {
Timber.d("loadComposerDraft for $roomId") Timber.d("loadComposerDraft for $roomId with thread root: $threadRoot")
innerRoom.loadComposerDraft()?.into() innerRoom.loadComposerDraft(threadRoot?.value)?.into()
} }
} }
override suspend fun clearComposerDraft(): Result<Unit> = withContext(roomDispatcher) { override suspend fun clearComposerDraft(threadRoot: ThreadId?): Result<Unit> = withContext(roomDispatcher) {
runCatchingExceptions { runCatchingExceptions {
Timber.d("clearComposerDraft for $roomId") Timber.d("clearComposerDraft for $roomId with thread root: $threadRoot")
innerRoom.clearComposerDraft() innerRoom.clearComposerDraft(threadRoot = threadRoot?.value)
} }
} }

View file

@ -485,6 +485,7 @@ class RustTimeline(
description: String?, description: String?,
zoomLevel: Int?, zoomLevel: Int?,
assetType: AssetType?, assetType: AssetType?,
replyParameters: ReplyParameters?,
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatchingExceptions { runCatchingExceptions {
inner.sendLocation( inner.sendLocation(
@ -493,6 +494,7 @@ class RustTimeline(
description = description, description = description,
zoomLevel = zoomLevel?.toUByte(), zoomLevel = zoomLevel?.toUByte(),
assetType = assetType?.toInner(), assetType = assetType?.toInner(),
replyParams = replyParameters?.map(),
) )
} }
} }

View file

@ -176,6 +176,7 @@ private fun ShieldState?.map(): MessageShield? {
ShieldStateCode.UNVERIFIED_IDENTITY -> MessageShield.UnverifiedIdentity(isCritical) ShieldStateCode.UNVERIFIED_IDENTITY -> MessageShield.UnverifiedIdentity(isCritical)
ShieldStateCode.SENT_IN_CLEAR -> MessageShield.SentInClear(isCritical) ShieldStateCode.SENT_IN_CLEAR -> MessageShield.SentInClear(isCritical)
ShieldStateCode.VERIFICATION_VIOLATION -> MessageShield.VerificationViolation(isCritical) ShieldStateCode.VERIFICATION_VIOLATION -> MessageShield.VerificationViolation(isCritical)
ShieldStateCode.MISMATCHED_SENDER -> MessageShield.MismatchedSender(isCritical)
} }
} }

View file

@ -11,6 +11,7 @@ import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
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.room.BaseRoom import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.MessageEventType
@ -198,11 +199,14 @@ class FakeBaseRoom(
return Result.success(Unit) return Result.success(Unit)
} }
override suspend fun saveComposerDraft(composerDraft: ComposerDraft) = saveComposerDraftLambda(composerDraft) override suspend fun saveComposerDraft(
composerDraft: ComposerDraft,
threadRoot: ThreadId?
) = saveComposerDraftLambda(composerDraft)
override suspend fun loadComposerDraft() = loadComposerDraftLambda() override suspend fun loadComposerDraft(threadRoot: ThreadId?) = loadComposerDraftLambda()
override suspend fun clearComposerDraft() = clearComposerDraftLambda() override suspend fun clearComposerDraft(threadRoot: ThreadId?) = clearComposerDraftLambda()
override suspend fun getUpdatedIsEncrypted(): Result<Boolean> = simulateLongTask { override suspend fun getUpdatedIsEncrypted(): Result<Boolean> = simulateLongTask {
Result.success(info().isEncrypted.orFalse()) Result.success(info().isEncrypted.orFalse())

View file

@ -304,7 +304,8 @@ class FakeTimeline(
description: String?, description: String?,
zoomLevel: Int?, zoomLevel: Int?,
assetType: AssetType?, assetType: AssetType?,
) -> Result<Unit> = { _, _, _, _, _ -> replyParameters: ReplyParameters?,
) -> Result<Unit> = { _, _, _, _, _, _ ->
lambdaError() lambdaError()
} }
@ -314,6 +315,7 @@ class FakeTimeline(
description: String?, description: String?,
zoomLevel: Int?, zoomLevel: Int?,
assetType: AssetType?, assetType: AssetType?,
replyParameters: ReplyParameters?,
): Result<Unit> = simulateLongTask { ): Result<Unit> = simulateLongTask {
sendLocationLambda( sendLocationLambda(
body, body,
@ -321,6 +323,7 @@ class FakeTimeline(
description, description,
zoomLevel, zoomLevel,
assetType, assetType,
replyParameters,
) )
} }

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:4c389d128a7f89f8351b31c6043e9b6870af2c71e5e6e7eefbf3c632e6209f8b oid sha256:dc2234e1f00b0edbe3389464b1ccf375a034bb4f6daca6c80345c85dc2d4a267
size 36218 size 44822

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:260b0e20954785563d6abc1a66fbc8823efecf2c8125802fe13b30aa1ab21786 oid sha256:caa64bc210b30027a5f0eb1ea8c20b3d170b37ee3e1ac8f535233b3209feefc7
size 35104 size 43436