diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt index 1106f4ff29..662c332582 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt @@ -9,4 +9,5 @@ package io.element.android.appconfig object LearnMoreConfig { const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5" + const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18" } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index e7abd8e60b..bf76f208e3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -27,13 +27,14 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.libraries.androidutils.system.openUrlInExternalApp +import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -122,7 +123,8 @@ class MessagesNode @AssistedInject constructor( } private fun onLinkClick( - context: Context, + activity: Activity, + darkTheme: Boolean, url: String, eventSink: (TimelineEvents) -> Unit, ) { @@ -133,11 +135,11 @@ class MessagesNode @AssistedInject constructor( callbacks.forEach { it.onUserDataClick(permalink.userId) } } is PermalinkData.RoomLink -> { - handleRoomLinkClick(context, permalink, eventSink) + handleRoomLinkClick(activity, permalink, eventSink) } is PermalinkData.FallbackLink, is PermalinkData.RoomEmailInviteLink -> { - context.openUrlInExternalApp(url) + activity.openUrlInChromeCustomTab(null, darkTheme, url) } } } @@ -195,6 +197,7 @@ class MessagesNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val activity = LocalContext.current as Activity + val isDark = ElementTheme.isLightTheme.not() CompositionLocalProvider( LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories, ) { @@ -212,7 +215,7 @@ class MessagesNode @AssistedInject constructor( onEventClick = this::onEventClick, onPreviewAttachments = this::onPreviewAttachments, onUserDataClick = this::onUserDataClick, - onLinkClick = { onLinkClick(activity, it, state.timelineState.eventSink) }, + onLinkClick = { url -> onLinkClick(activity, isDark, url, state.timelineState.eventSink) }, onSendLocationClick = this::onSendLocationClick, onCreatePollClick = this::onCreatePollClick, onJoinCallClick = this::onJoinCallClick, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 709ee3734f..e56d37005b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -417,6 +417,7 @@ private fun MessagesViewContent( MessagesViewComposerBottomSheetContents( subcomposing = subcomposing, state = state, + onLinkClick = onLinkClick, ) }, sheetContentKey = sheetResizeContentKey.intValue, @@ -430,6 +431,7 @@ private fun MessagesViewContent( private fun MessagesViewComposerBottomSheetContents( subcomposing: Boolean, state: MessagesState, + onLinkClick: (String) -> Unit, ) { if (state.userEventPermissions.canSendMessage) { Column(modifier = Modifier.fillMaxWidth()) { @@ -453,7 +455,10 @@ private fun MessagesViewComposerBottomSheetContents( // Do not show the identity change if user is composing a Rich message or is seeing suggestion(s). if (state.composerState.suggestions.isEmpty() && state.composerState.textEditorState is TextEditorState.Markdown) { - IdentityChangeStateView(state.identityChangeState) + IdentityChangeStateView( + state = state.identityChangeState, + onLinkClick = onLinkClick, + ) } MessageComposerView( state = state.composerState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt index 3a13359b4c..58ee9ffd88 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt @@ -10,11 +10,13 @@ package io.element.android.features.messages.impl.crypto.identity import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.appconfig.LearnMoreConfig import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview @@ -26,6 +28,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun IdentityChangeStateView( state: IdentityChangeState, + onLinkClick: (String) -> Unit, modifier: Modifier = Modifier, ) { // Pick the first identity change to PinViolation @@ -38,27 +41,31 @@ fun IdentityChangeStateView( modifier = modifier, avatar = identityChange.roomMember.getAvatarData(AvatarSize.ComposerAlert), content = buildAnnotatedString { - val coloredPart = stringResource(CommonStrings.action_learn_more) + val learnMoreStr = stringResource(CommonStrings.action_learn_more) val fullText = stringResource( - CommonStrings.crypto_identity_change_pin_violation, + id = CommonStrings.crypto_identity_change_pin_violation, identityChange.roomMember.disambiguatedDisplayName, - coloredPart, + learnMoreStr, ) - val startIndex = fullText.indexOf(coloredPart) + val learnMoreStartIndex = fullText.indexOf(learnMoreStr) append(fullText) addStyle( style = SpanStyle( textDecoration = TextDecoration.Underline, fontWeight = FontWeight.Bold, ), - start = startIndex, - end = startIndex + coloredPart.length, + start = learnMoreStartIndex, + end = learnMoreStartIndex + learnMoreStr.length, ) - addStringAnnotation( - tag = "LEARN_MORE", - annotation = "TODO", - start = startIndex, - end = startIndex + coloredPart.length + addLink( + url = LinkAnnotation.Url( + url = LearnMoreConfig.IDENTITY_CHANGE_URL, + linkInteractionListener = { t -> + onLinkClick(LearnMoreConfig.IDENTITY_CHANGE_URL) + } + ), + start = learnMoreStartIndex, + end = learnMoreStartIndex + learnMoreStr.length, ) }, onSubmitClick = { state.eventSink(IdentityChangeEvent.Submit(identityChange.roomMember.userId)) }, @@ -74,5 +81,6 @@ internal fun IdentityChangeStateViewPreview( ) = ElementPreview { IdentityChangeStateView( state = state, + onLinkClick = {}, ) }