messages: separate custom tab links from normal links

Some links in the `MessagesView` are part of Element X itself,
such as the help pages in `LearnMoreConfig` [1]. These links
should open in an "in-app" Chrome Custom Tab, because they are
basically part of the app.

Web links from chat messages, on the other hand, should open in
the user's preferred web browser as regular tabs.

Separate "regular" links from "custom tab" links with a new
parameter `onLinkClick(..., customTab)`. If true, the link
opens in a custom tab.

Links within `TimelineView` are always opened in a normal tab.

[1]: appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt
This commit is contained in:
Colin S
2025-01-28 19:45:47 -06:00
parent e37e43bf79
commit 251c873672
5 changed files with 18 additions and 12 deletions

View File

@@ -139,6 +139,7 @@ class MessagesNode @AssistedInject constructor(
darkTheme: Boolean,
url: String,
eventSink: (TimelineEvents) -> Unit,
customTab: Boolean
) {
when (val permalink = permalinkParser.parse(url)) {
is PermalinkData.UserLink -> {
@@ -150,7 +151,11 @@ class MessagesNode @AssistedInject constructor(
handleRoomLinkClick(activity, permalink, eventSink)
}
is PermalinkData.FallbackLink -> {
activity.openUrlInExternalApp(url)
if (customTab) {
activity.openUrlInChromeCustomTab(null, darkTheme, url)
} else {
activity.openUrlInExternalApp(url)
}
}
is PermalinkData.RoomEmailInviteLink -> {
activity.openUrlInChromeCustomTab(null, darkTheme, url)
@@ -236,7 +241,7 @@ class MessagesNode @AssistedInject constructor(
onRoomDetailsClick = this::onRoomDetailsClick,
onEventContentClick = this::onEventClick,
onUserDataClick = this::onUserDataClick,
onLinkClick = { url -> onLinkClick(activity, isDark, url, state.timelineState.eventSink) },
onLinkClick = { url, customTab -> onLinkClick(activity, isDark, url, state.timelineState.eventSink, customTab) },
onSendLocationClick = this::onSendLocationClick,
onCreatePollClick = this::onCreatePollClick,
onJoinCallClick = this::onJoinCallClick,

View File

@@ -111,7 +111,7 @@ fun MessagesView(
onRoomDetailsClick: () -> Unit,
onEventContentClick: (event: TimelineItem.Event) -> Boolean,
onUserDataClick: (UserId) -> Unit,
onLinkClick: (String) -> Unit,
onLinkClick: (String, Boolean) -> Unit,
onSendLocationClick: () -> Unit,
onCreatePollClick: () -> Unit,
onJoinCallClick: () -> Unit,
@@ -273,7 +273,7 @@ private fun MessagesViewContent(
state: MessagesState,
onContentClick: (TimelineItem.Event) -> Unit,
onUserDataClick: (UserId) -> Unit,
onLinkClick: (String) -> Unit,
onLinkClick: (String, Boolean) -> Unit,
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
@@ -347,7 +347,7 @@ private fun MessagesViewContent(
state = state.timelineState,
timelineProtectionState = state.timelineProtectionState,
onUserDataClick = onUserDataClick,
onLinkClick = onLinkClick,
onLinkClick = { url -> onLinkClick(url, false) },
onContentClick = onContentClick,
onMessageLongClick = onMessageLongClick,
onSwipeToReply = onSwipeToReply,
@@ -396,7 +396,7 @@ private fun MessagesViewContent(
private fun MessagesViewComposerBottomSheetContents(
subcomposing: Boolean,
state: MessagesState,
onLinkClick: (String) -> Unit,
onLinkClick: (String, Boolean) -> Unit,
) {
if (state.userEventPermissions.canSendMessage) {
Column(modifier = Modifier.fillMaxWidth()) {
@@ -537,7 +537,7 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class)
onRoomDetailsClick = {},
onEventContentClick = { false },
onUserDataClick = {},
onLinkClick = {},
onLinkClick = { _, _ -> },
onSendLocationClick = {},
onCreatePollClick = {},
onJoinCallClick = {},

View File

@@ -26,7 +26,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun IdentityChangeStateView(
state: IdentityChangeState,
onLinkClick: (String) -> Unit,
onLinkClick: (String, Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
// Pick the first identity change to PinViolation
@@ -73,7 +73,7 @@ fun IdentityChangeStateView(
url = LinkAnnotation.Url(
url = LearnMoreConfig.IDENTITY_CHANGE_URL,
linkInteractionListener = {
onLinkClick(LearnMoreConfig.IDENTITY_CHANGE_URL)
onLinkClick(LearnMoreConfig.IDENTITY_CHANGE_URL, true)
}
),
start = learnMoreStartIndex,
@@ -93,6 +93,6 @@ internal fun IdentityChangeStateViewPreview(
) = ElementPreview {
IdentityChangeStateView(
state = state,
onLinkClick = {},
onLinkClick = { _, _ -> },
)
}

View File

@@ -35,7 +35,7 @@ internal fun MessagesViewWithIdentityChangePreview(
onRoomDetailsClick = {},
onEventContentClick = { false },
onUserDataClick = {},
onLinkClick = {},
onLinkClick = { _, _ -> },
onSendLocationClick = {},
onCreatePollClick = {},
onJoinCallClick = {},

View File

@@ -58,6 +58,7 @@ import io.element.android.tests.testutils.EnsureCalledOnceWithParam
import io.element.android.tests.testutils.EnsureNeverCalled
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
import io.element.android.tests.testutils.EnsureNeverCalledWithParamAndResult
import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
@@ -514,7 +515,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMessa
onRoomDetailsClick: () -> Unit = EnsureNeverCalled(),
onEventClick: (event: TimelineItem.Event) -> Boolean = EnsureNeverCalledWithParamAndResult(),
onUserDataClick: (UserId) -> Unit = EnsureNeverCalledWithParam(),
onLinkClick: (String) -> Unit = EnsureNeverCalledWithParam(),
onLinkClick: (String, Boolean) -> Unit = EnsureNeverCalledWithTwoParams(),
onSendLocationClick: () -> Unit = EnsureNeverCalled(),
onCreatePollClick: () -> Unit = EnsureNeverCalled(),
onJoinCallClick: () -> Unit = EnsureNeverCalled(),