Merge pull request #2653 from element-hq/feature/bma/copyPermalink
Copy permalink
This commit is contained in:
1
changelog.d/2650.feature
Normal file
1
changelog.d/2650.feature
Normal file
@@ -0,0 +1 @@
|
||||
Add action to copy permalink
|
||||
@@ -89,6 +89,7 @@ import io.element.android.libraries.matrix.ui.room.canRedactOtherAsState
|
||||
import io.element.android.libraries.matrix.ui.room.canRedactOwnAsState
|
||||
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
|
||||
import io.element.android.libraries.textcomposer.model.MessageComposerMode
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -273,6 +274,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
) = launch {
|
||||
when (action) {
|
||||
TimelineItemAction.Copy -> handleCopyContents(targetEvent)
|
||||
TimelineItemAction.CopyLink -> handleCopyLink(targetEvent)
|
||||
TimelineItemAction.Redact -> handleActionRedact(targetEvent)
|
||||
TimelineItemAction.Edit -> handleActionEdit(targetEvent, composerState, enableTextFormatting)
|
||||
TimelineItemAction.Reply,
|
||||
@@ -435,6 +437,20 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
event.eventId?.let { timelineState.eventSink(TimelineEvents.PollEndClicked(it)) }
|
||||
}
|
||||
|
||||
private suspend fun handleCopyLink(event: TimelineItem.Event) {
|
||||
event.eventId ?: return
|
||||
room.getPermalinkFor(event.eventId).fold(
|
||||
onSuccess = { permalink ->
|
||||
clipboardHelper.copyPlainText(permalink)
|
||||
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_link_copied_to_clipboard))
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Failed to get permalink for event ${event.eventId}")
|
||||
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_error))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun handleCopyContents(event: TimelineItem.Event) {
|
||||
val content = when (event.content) {
|
||||
is TimelineItemTextBasedContent -> event.content.body
|
||||
|
||||
@@ -96,6 +96,7 @@ class ActionListPresenter @Inject constructor(
|
||||
is TimelineItemStateContent -> {
|
||||
buildList {
|
||||
add(TimelineItemAction.Copy)
|
||||
add(TimelineItemAction.CopyLink)
|
||||
if (isDeveloperModeEnabled) {
|
||||
add(TimelineItemAction.ViewSource)
|
||||
}
|
||||
@@ -119,6 +120,7 @@ class ActionListPresenter @Inject constructor(
|
||||
if (timelineItem.content.canBeCopied()) {
|
||||
add(TimelineItemAction.Copy)
|
||||
}
|
||||
add(TimelineItemAction.CopyLink)
|
||||
if (isDeveloperModeEnabled) {
|
||||
add(TimelineItemAction.ViewSource)
|
||||
}
|
||||
@@ -136,6 +138,7 @@ class ActionListPresenter @Inject constructor(
|
||||
add(TimelineItemAction.Reply)
|
||||
add(TimelineItemAction.Forward)
|
||||
}
|
||||
add(TimelineItemAction.CopyLink)
|
||||
if (isDeveloperModeEnabled) {
|
||||
add(TimelineItemAction.ViewSource)
|
||||
}
|
||||
@@ -176,6 +179,7 @@ class ActionListPresenter @Inject constructor(
|
||||
if (timelineItem.content.canBeCopied()) {
|
||||
add(TimelineItemAction.Copy)
|
||||
}
|
||||
add(TimelineItemAction.CopyLink)
|
||||
if (isDeveloperModeEnabled) {
|
||||
add(TimelineItemAction.ViewSource)
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ fun aTimelineItemActionList(): ImmutableList<TimelineItemAction> {
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Edit,
|
||||
TimelineItemAction.Redact,
|
||||
TimelineItemAction.ReportContent,
|
||||
@@ -146,6 +147,7 @@ fun aTimelineItemPollActionList(): ImmutableList<TimelineItemAction> {
|
||||
TimelineItemAction.EndPoll,
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.ReportContent,
|
||||
TimelineItemAction.Redact,
|
||||
|
||||
@@ -31,6 +31,7 @@ sealed class TimelineItemAction(
|
||||
) {
|
||||
data object Forward : TimelineItemAction(CommonStrings.action_forward, CompoundDrawables.ic_compound_forward)
|
||||
data object Copy : TimelineItemAction(CommonStrings.action_copy, CompoundDrawables.ic_compound_copy)
|
||||
data object CopyLink : TimelineItemAction(CommonStrings.action_copy_link_to_message, CompoundDrawables.ic_compound_link)
|
||||
data object Redact : TimelineItemAction(CommonStrings.action_remove, CompoundDrawables.ic_compound_delete, destructive = true)
|
||||
data object Reply : TimelineItemAction(CommonStrings.action_reply, CompoundDrawables.ic_compound_reply)
|
||||
data object ReplyInThread : TimelineItemAction(CommonStrings.action_reply_in_thread, CompoundDrawables.ic_compound_reply)
|
||||
|
||||
@@ -231,6 +231,27 @@ class MessagesPresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle action copy link`() = runTest {
|
||||
val clipboardHelper = FakeClipboardHelper()
|
||||
val event = aMessageEvent()
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
permalinkResult = { Result.success("a link") },
|
||||
)
|
||||
val presenter = createMessagesPresenter(
|
||||
clipboardHelper = clipboardHelper,
|
||||
matrixRoom = matrixRoom,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.CopyLink, event))
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assertThat(clipboardHelper.clipboardContents).isEqualTo("a link")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle action reply`() = runTest {
|
||||
val presenter = createMessagesPresenter()
|
||||
|
||||
@@ -153,6 +153,7 @@ class ActionListPresenterTest {
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.ReportContent,
|
||||
)
|
||||
@@ -193,6 +194,7 @@ class ActionListPresenterTest {
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.ReportContent,
|
||||
)
|
||||
@@ -232,6 +234,7 @@ class ActionListPresenterTest {
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.ReportContent,
|
||||
TimelineItemAction.Redact,
|
||||
@@ -272,6 +275,7 @@ class ActionListPresenterTest {
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.ReportContent,
|
||||
TimelineItemAction.Redact,
|
||||
@@ -315,6 +319,7 @@ class ActionListPresenterTest {
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Edit,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
@@ -357,6 +362,7 @@ class ActionListPresenterTest {
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Edit,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
)
|
||||
)
|
||||
@@ -396,6 +402,7 @@ class ActionListPresenterTest {
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
@@ -435,6 +442,7 @@ class ActionListPresenterTest {
|
||||
displayEmojiReactions = false,
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.ViewSource,
|
||||
)
|
||||
)
|
||||
@@ -473,6 +481,7 @@ class ActionListPresenterTest {
|
||||
displayEmojiReactions = false,
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -513,6 +522,7 @@ class ActionListPresenterTest {
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Edit,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
)
|
||||
@@ -595,6 +605,7 @@ class ActionListPresenterTest {
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Edit,
|
||||
TimelineItemAction.Copy,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
)
|
||||
@@ -632,6 +643,7 @@ class ActionListPresenterTest {
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Edit,
|
||||
TimelineItemAction.EndPoll,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
)
|
||||
@@ -668,6 +680,7 @@ class ActionListPresenterTest {
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.EndPoll,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
)
|
||||
@@ -703,6 +716,7 @@ class ActionListPresenterTest {
|
||||
displayEmojiReactions = true,
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
)
|
||||
@@ -738,6 +752,7 @@ class ActionListPresenterTest {
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -328,5 +328,12 @@ interface MatrixRoom : Closeable {
|
||||
*/
|
||||
fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver>
|
||||
|
||||
/**
|
||||
* Get the permalink for the provided [eventId].
|
||||
* @param eventId The event id to get the permalink for.
|
||||
* @return The permalink, or a failure.
|
||||
*/
|
||||
suspend fun getPermalinkFor(eventId: EventId): Result<String>
|
||||
|
||||
override fun close() = destroy()
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package io.element.android.libraries.matrix.impl.room
|
||||
|
||||
import io.element.android.appconfig.MatrixConfiguration
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.coroutine.childScope
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
@@ -711,6 +712,19 @@ class RustMatrixRoom(
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getPermalinkFor(eventId: EventId): Result<String> {
|
||||
// FIXME Use the SDK API once https://github.com/matrix-org/matrix-rust-sdk/issues/3259 has been done
|
||||
// Now use a simple builder
|
||||
return runCatching {
|
||||
buildString {
|
||||
append(MatrixConfiguration.MATRIX_TO_PERMALINK_BASE_URL)
|
||||
append(roomId.value)
|
||||
append("/")
|
||||
append(eventId.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> {
|
||||
return runCatching {
|
||||
MediaUploadHandlerImpl(files, handle())
|
||||
|
||||
@@ -84,6 +84,7 @@ class FakeMatrixRoom(
|
||||
override val activeMemberCount: Long = 234L,
|
||||
val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(),
|
||||
private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(),
|
||||
private var permalinkResult: () -> Result<String> = { Result.success("link") },
|
||||
canRedactOwn: Boolean = false,
|
||||
canRedactOther: Boolean = false,
|
||||
) : MatrixRoom {
|
||||
@@ -273,6 +274,10 @@ class FakeMatrixRoom(
|
||||
return cancelSendResult
|
||||
}
|
||||
|
||||
override suspend fun getPermalinkFor(eventId: EventId): Result<String> {
|
||||
return permalinkResult()
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
originalEventId: EventId?,
|
||||
transactionId: TransactionId?,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user