mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Hide the forward action on live location shares
The timeline action sheet offered Forward on a live location share, but forwarding one always failed: Mode.Live maps to the SDK's MsgLikeKind.LiveLocation, which RoomContentForwarder rejects outright. So the action was shown for exactly the set of events on which it could never work. canBeForwarded() now returns true for a location event only when its mode is Static. Ended live shares keep Mode.Live and fail for the same reason, so they are excluded too. ActionListPresenter needs no change: its check is the function's only call site in the repo, so the single gate covers the timeline sheet, the threaded timeline and the pinned messages list. Hiding the action mirrors Element Web and the sticker exclusion already present in the same function. Converting a live share into a static m.location on forward would be a product decision and is out of scope. Relates to https://github.com/element-hq/element-x-android/issues/7190
This commit is contained in:
+3
-1
@@ -58,10 +58,12 @@ fun TimelineItemEventContent.canBeForwarded(): Boolean =
|
||||
is TimelineItemFileContent,
|
||||
is TimelineItemAudioContent,
|
||||
is TimelineItemVideoContent,
|
||||
is TimelineItemLocationContent,
|
||||
is TimelineItemVoiceContent,
|
||||
is TimelineItemGalleryContent,
|
||||
is TimelineItemAttachmentsContent -> true
|
||||
// Live location shares can't be forwarded, the SDK rejects them, so we only show the option for static locations
|
||||
// See https://github.com/element-hq/element-x-android/issues/7190
|
||||
is TimelineItemLocationContent -> mode is TimelineItemLocationContent.Mode.Static
|
||||
// Stickers can't be forwarded (yet) so we don't show the option
|
||||
// See https://github.com/element-hq/element-x-android/issues/2161
|
||||
is TimelineItemStickerContent -> false
|
||||
|
||||
+120
@@ -21,7 +21,10 @@ import io.element.android.features.messages.impl.timeline.model.event.RtcNotific
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aLiveLocationMode
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aStaticLocationMode
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemLocationContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent
|
||||
@@ -601,6 +604,123 @@ class ActionListPresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - compute for a static location item`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
isEditable = false,
|
||||
content = aTimelineItemLocationContent(mode = aStaticLocationMode()),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
canRedactOther = false,
|
||||
canSendMessage = true,
|
||||
canSendReaction = true,
|
||||
canPinUnpin = true,
|
||||
),
|
||||
)
|
||||
)
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.target).isEqualTo(
|
||||
ActionListState.Target.Success(
|
||||
event = messageEvent,
|
||||
sentTimeFull = "0 Full true",
|
||||
displayEmojiReactions = true,
|
||||
verifiedUserSendFailure = VerifiedUserSendFailure.None,
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Pin,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.Redact,
|
||||
),
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - compute for a live location item does not offer to forward it`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
isEditable = false,
|
||||
content = aTimelineItemLocationContent(mode = aLiveLocationMode(isActive = true)),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
canRedactOther = false,
|
||||
canSendMessage = true,
|
||||
canSendReaction = true,
|
||||
canPinUnpin = true,
|
||||
),
|
||||
)
|
||||
)
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.target).isEqualTo(
|
||||
ActionListState.Target.Success(
|
||||
event = messageEvent,
|
||||
sentTimeFull = "0 Full true",
|
||||
displayEmojiReactions = true,
|
||||
verifiedUserSendFailure = VerifiedUserSendFailure.None,
|
||||
actions = persistentListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.CopyLink,
|
||||
TimelineItemAction.Pin,
|
||||
TimelineItemAction.ViewSource,
|
||||
TimelineItemAction.Redact,
|
||||
),
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - compute for an ended live location item does not offer to forward it`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
isEditable = false,
|
||||
content = aTimelineItemLocationContent(mode = aLiveLocationMode(isActive = false)),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
canRedactOther = false,
|
||||
canSendMessage = true,
|
||||
canSendReaction = true,
|
||||
canPinUnpin = true,
|
||||
),
|
||||
)
|
||||
)
|
||||
val successState = awaitItem()
|
||||
assertThat((successState.target as ActionListState.Target.Success).actions)
|
||||
.doesNotContain(TimelineItemAction.Forward)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - compute for a media with caption item`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
|
||||
Reference in New Issue
Block a user