Identity change: handle click on "learn more"

This commit is contained in:
Benoit Marty 2024-10-08 09:35:18 +02:00
parent 67140fe165
commit 57e45aa834
4 changed files with 34 additions and 17 deletions

View file

@ -9,4 +9,5 @@ package io.element.android.appconfig
object LearnMoreConfig { object LearnMoreConfig {
const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5" const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5"
const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18"
} }

View file

@ -27,13 +27,14 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode 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.attachments.Attachment
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents 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.TimelineEvents
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories 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.di.TimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.model.TimelineItem 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.androidutils.system.toast
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
@ -122,7 +123,8 @@ class MessagesNode @AssistedInject constructor(
} }
private fun onLinkClick( private fun onLinkClick(
context: Context, activity: Activity,
darkTheme: Boolean,
url: String, url: String,
eventSink: (TimelineEvents) -> Unit, eventSink: (TimelineEvents) -> Unit,
) { ) {
@ -133,11 +135,11 @@ class MessagesNode @AssistedInject constructor(
callbacks.forEach { it.onUserDataClick(permalink.userId) } callbacks.forEach { it.onUserDataClick(permalink.userId) }
} }
is PermalinkData.RoomLink -> { is PermalinkData.RoomLink -> {
handleRoomLinkClick(context, permalink, eventSink) handleRoomLinkClick(activity, permalink, eventSink)
} }
is PermalinkData.FallbackLink, is PermalinkData.FallbackLink,
is PermalinkData.RoomEmailInviteLink -> { is PermalinkData.RoomEmailInviteLink -> {
context.openUrlInExternalApp(url) activity.openUrlInChromeCustomTab(null, darkTheme, url)
} }
} }
} }
@ -195,6 +197,7 @@ class MessagesNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val activity = LocalContext.current as Activity val activity = LocalContext.current as Activity
val isDark = ElementTheme.isLightTheme.not()
CompositionLocalProvider( CompositionLocalProvider(
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories, LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,
) { ) {
@ -212,7 +215,7 @@ class MessagesNode @AssistedInject constructor(
onEventClick = this::onEventClick, onEventClick = this::onEventClick,
onPreviewAttachments = this::onPreviewAttachments, onPreviewAttachments = this::onPreviewAttachments,
onUserDataClick = this::onUserDataClick, onUserDataClick = this::onUserDataClick,
onLinkClick = { onLinkClick(activity, it, state.timelineState.eventSink) }, onLinkClick = { url -> onLinkClick(activity, isDark, url, state.timelineState.eventSink) },
onSendLocationClick = this::onSendLocationClick, onSendLocationClick = this::onSendLocationClick,
onCreatePollClick = this::onCreatePollClick, onCreatePollClick = this::onCreatePollClick,
onJoinCallClick = this::onJoinCallClick, onJoinCallClick = this::onJoinCallClick,

View file

@ -417,6 +417,7 @@ private fun MessagesViewContent(
MessagesViewComposerBottomSheetContents( MessagesViewComposerBottomSheetContents(
subcomposing = subcomposing, subcomposing = subcomposing,
state = state, state = state,
onLinkClick = onLinkClick,
) )
}, },
sheetContentKey = sheetResizeContentKey.intValue, sheetContentKey = sheetResizeContentKey.intValue,
@ -430,6 +431,7 @@ private fun MessagesViewContent(
private fun MessagesViewComposerBottomSheetContents( private fun MessagesViewComposerBottomSheetContents(
subcomposing: Boolean, subcomposing: Boolean,
state: MessagesState, state: MessagesState,
onLinkClick: (String) -> Unit,
) { ) {
if (state.userEventPermissions.canSendMessage) { if (state.userEventPermissions.canSendMessage) {
Column(modifier = Modifier.fillMaxWidth()) { 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). // Do not show the identity change if user is composing a Rich message or is seeing suggestion(s).
if (state.composerState.suggestions.isEmpty() && if (state.composerState.suggestions.isEmpty() &&
state.composerState.textEditorState is TextEditorState.Markdown) { state.composerState.textEditorState is TextEditorState.Markdown) {
IdentityChangeStateView(state.identityChangeState) IdentityChangeStateView(
state = state.identityChangeState,
onLinkClick = onLinkClick,
)
} }
MessageComposerView( MessageComposerView(
state = state.composerState, state = state.composerState,

View file

@ -10,11 +10,13 @@ package io.element.android.features.messages.impl.crypto.identity
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.PreviewParameter 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.atomic.molecules.ComposerAlertMolecule
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
@ -26,6 +28,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable @Composable
fun IdentityChangeStateView( fun IdentityChangeStateView(
state: IdentityChangeState, state: IdentityChangeState,
onLinkClick: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
// Pick the first identity change to PinViolation // Pick the first identity change to PinViolation
@ -38,27 +41,31 @@ fun IdentityChangeStateView(
modifier = modifier, modifier = modifier,
avatar = identityChange.roomMember.getAvatarData(AvatarSize.ComposerAlert), avatar = identityChange.roomMember.getAvatarData(AvatarSize.ComposerAlert),
content = buildAnnotatedString { content = buildAnnotatedString {
val coloredPart = stringResource(CommonStrings.action_learn_more) val learnMoreStr = stringResource(CommonStrings.action_learn_more)
val fullText = stringResource( val fullText = stringResource(
CommonStrings.crypto_identity_change_pin_violation, id = CommonStrings.crypto_identity_change_pin_violation,
identityChange.roomMember.disambiguatedDisplayName, identityChange.roomMember.disambiguatedDisplayName,
coloredPart, learnMoreStr,
) )
val startIndex = fullText.indexOf(coloredPart) val learnMoreStartIndex = fullText.indexOf(learnMoreStr)
append(fullText) append(fullText)
addStyle( addStyle(
style = SpanStyle( style = SpanStyle(
textDecoration = TextDecoration.Underline, textDecoration = TextDecoration.Underline,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
), ),
start = startIndex, start = learnMoreStartIndex,
end = startIndex + coloredPart.length, end = learnMoreStartIndex + learnMoreStr.length,
) )
addStringAnnotation( addLink(
tag = "LEARN_MORE", url = LinkAnnotation.Url(
annotation = "TODO", url = LearnMoreConfig.IDENTITY_CHANGE_URL,
start = startIndex, linkInteractionListener = { t ->
end = startIndex + coloredPart.length onLinkClick(LearnMoreConfig.IDENTITY_CHANGE_URL)
}
),
start = learnMoreStartIndex,
end = learnMoreStartIndex + learnMoreStr.length,
) )
}, },
onSubmitClick = { state.eventSink(IdentityChangeEvent.Submit(identityChange.roomMember.userId)) }, onSubmitClick = { state.eventSink(IdentityChangeEvent.Submit(identityChange.roomMember.userId)) },
@ -74,5 +81,6 @@ internal fun IdentityChangeStateViewPreview(
) = ElementPreview { ) = ElementPreview {
IdentityChangeStateView( IdentityChangeStateView(
state = state, state = state,
onLinkClick = {},
) )
} }